What’s a Bill of Material BOM file?
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> |
After that, you pick what you want in dependencies, without version numbers:
<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> |
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> |
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> |
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 |
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.