Get up to speed with the core of Maven quickly, and then go beyond the foundations into the more powerful functionality of the build tool, such as profiles, scopes, multi-module projects and quite a bit more:
Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Overview
In this quick tutorial, weβre going to look at how Maven, a tool based on the concept of Project Object Model (POM), can make use of a BOM or βBill Of Materialsβ.
For more details about Maven, you can check our article Apache Maven Tutorial.
2. Dependency Management Concepts
To understand what a BOM is and what we can use it for, we first need to learn basic concepts.
2.1. What Is Maven POM?
Maven POM is an XML file that contains information and configurations (about the project) that are used by Maven to import dependencies and build the project.
2.2. What Is Maven BOM?
BOM stands for Bill Of Materials. A BOM is a special kind of POM that is used to control the versions of a projectβs dependencies and provide a central place to define and update those versions.
BOM provides the flexibility to add a dependency to our module without worrying about the version that we should depend on.
2.3. Transitive Dependencies
Maven can discover the libraries that are needed by our own dependencies in our pom.xml and includes them automatically. Thereβs no limit to the number of dependency levels that the libraries are gathered from.
The conflict here comes when 2 dependencies refer to different versions of a specific artifact. Which one will be included by Maven?
The answer here is the βnearest definitionβ. This means that the version used will be the closest one to our project in the tree of dependencies. This is called dependency mediation.
Letβs see the following example to clarify the dependency mediation:
A -> B -> C -> D 1.4 and A -> E -> D 1.0
This example shows that project A depends on B and E. B and E have their own dependencies which encounter different versions of the D artifact. Artifact D 1.0 will be used in the build of A project because the path through E is shorter.
There are different techniques to determine which version of the artifacts should be included:
- We can always guarantee a version by declaring it explicitly in our projectβs POM. For instance, to guarantee that D 1.4 is used, we should add it explicitly as a dependency in the pom.xml file.
- We can use the Dependency Management section to control artifact versions as we will explain later in this article.
2.4. Dependency Management
Simply put, Dependency Management is a mechanism to centralize the dependency information.
When we have a set of projects that inherit a common parent, we can put all dependency information in a shared POM file called BOM.
Following is an example of how to write a BOM file:
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>baeldung</groupId>
<artifactId>Baeldung-BOM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BaelDung-BOM</name>
<description>parent pom</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
As we can see, the BOM is a normal POM file with a dependencyManagement section where we can include all an artifactβs information and versions.
2.5. Using the BOM File
There are 2 ways to use the previous BOM file in our project and then we will be ready to declare our dependencies without having to worry about version numbers.
We can inherit from the parent:
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>baeldung</groupId>
<artifactId>Test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Test</name>
<parent>
<groupId>baeldung</groupId>
<artifactId>Baeldung-BOM</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
</project>
As we can see our project Test inherits the Baeldung-BOM.
We can also import the BOM.
In larger projects, the approach of inheritance is not efficient because the project can inherit only a single parent. Importing is the alternative as we can import as many BOMs as we need.
Letβs see how we can import a BOM file into our project POM:
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>baeldung</groupId>
<artifactId>Test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Test</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>baeldung</groupId>
<artifactId>Baeldung-BOM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.6. Overwriting BOM Dependency
The order of precedence of the artifactβs version is:
- The version of the artifactβs direct declaration in our project pom
- The version of the artifact in the parent project
- The version in the imported pom, taking into consideration the order of importing files
- dependency mediation
- We can overwrite the artifactβs version by explicitly defining the artifact in our projectβs pom with the desired version
- If the same artifact is defined with different versions in 2 imported BOMs, then the version in the BOM file that was declared first will win
3. Spring BOM
We may find that a third-party library, or another Spring project, pulls in a transitive dependency to an older release. If we forget to explicitly declare a direct dependency, unexpected issues can arise.
To overcome such problems, Maven supports the concept of BOM dependency.
We can import the spring-framework-bom in our dependencyManagement section to ensure that all Spring dependencies are at the same version:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.3.27</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
We donβt need to specify the version attribute when we use the Spring artifacts as in the following example:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies>
4. Conclusion
In this quick article, we showed the Maven Bill-Of-Material Concept and how to centralize the artifactβs information and versions in a common POM.
Simply put, we can then either inherit or import it to make use of the BOM benefits.
