Wikipedia says:

A bill of materials or product structure (sometimes bill of material, BOM or associated list) is a list of the raw materials, sub-assemblies, intermediate assemblies, sub-components, parts, and the quantities of each needed to manufacture an end product. A BOM may be used for communication between manufacturing partners or confined to a single manufacturing plant. A bill of materials is often tied to a production order

https://en.wikipedia.org/wiki/Bill_of_materials

In the world of Java and Maven that boils down to some important Maven concepts.

The packing type

The dependency management tag

The fact that build files can import other build files

The packing type

Maven build descriptors (or “project object models”, in short pom (take note of the p)) can have different packaging: pom, jar, maven-plugin, ejb, war, ear, and rar. Standard is jar.

The type pom doesn’t produce an artefact but is the artefact itself. It is used for parent poms in multi module setups for example.

Poms can also be imported into other poms, into their dependency management, to be precise:

Dependency management

The manual has everything to get you covered. Here’s the important details from Dependency Management:

For BOMs we are not talking about the <dependencies /> -tag but <dependencyManagement/> . Inside the dependency management tag, dependencies, their version numbers and exclusions are specified. Much as you would do inside the top-level dependencies tag.

The dependency management tag however does not declare dependencies. This still needs to be done, but one can omit the versions now. For examples, look at the link above.

Now two important facts: The dependency management propagates to child modules and one can import POMs with packaging pom into dependency management.

Import others peoples dependencies

Have a look at Project Reactors BOM: reactor-bom.

Project Reactor doesn’t consist of one single artefact and or multiple artefacts having the same version numbers. Instead of forcing users to keep track of all those version numbers and import single dependencies, one does import the whole bom:

<dependencyManagement > <dependencies > <dependency > <groupId > io.projectreactor </groupId > <artifactId > reactor-bom </artifactId > <version > Californium-SR32 </version > <type > pom </type > <scope > import </scope > </dependency > </dependencies > </dependencyManagement > <dependencyManagement> <dependencies> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-bom</artifactId> <version>Californium-SR32</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

After that, you pick what you want in dependencies, without version numbers:

<dependencies > <dependency > <groupId > io.projectreactor </groupId > <artifactId > reactor-core </artifactId > </dependency > </dependencies > <dependencies> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> </dependency> </dependencies>

So now you have the bill of materials, from which you can pick.

Doing more cool things with it

Now Maven allows to define properties inside <properties/> . Those properties can be reused in dependency declarations and inside dependency management.

Instead of hardcoding Californium-SR32 inside the example above, you would do:

<properties > <reactor-bom.version > Californium-SR9 </reactor-bom.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > io.projectreactor </groupId > <artifactId > reactor-bom </artifactId > <version > ${reactor-bom.version} </version > <type > pom </type > <scope > import </scope > </dependency > </dependencies > </dependencyManagement > <properties> <reactor-bom.version>Californium-SR9</reactor-bom.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-bom</artifactId> <version>${reactor-bom.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

Now you can switch to another version of the dependency or in this case of the whole dependency management by just setting one property.

This is

How Spring Boot works

Spring Boot uses exactly that mechanism for all supported dependencies. See Neo4j-OGM you would define the following in your Maven POM file:

<properties > <neo4j-ogm.version > 3.1.13 </neo4j-ogm.version > </properties > <properties> <neo4j-ogm.version>3.1.13</neo4j-ogm.version> </properties>

This updates the version of all dependencies of Neo4j-OGM, and not only the one you might remembered.

It works exactly the same with projects using a BOM, for example Jackson:

<properties > <jackson.version > 2.9.9.20190807 </jackson.version > </properties > <properties> <jackson.version>2.9.9.20190807</jackson.version> </properties>

In a Gradle project, things need a second file, gradle.properties . I have written about it before. For the above example, that file would look like this:

jackson.version = 2.9.9.20190807 jackson.version = 2.9.9.20190807

Here’s the full example.

TL;DR

Many projects use BOMs these days. Import those whenever possible and then pick the dependencies your need from the projects without specifying individual versions.

In the case of Spring Boot, never overwrite managed dependencies in your own dependencies, but use a property. Both for plain dependencies as well as for BOMs.