Spring Boot is an excellent framework for quickly starting new projects. One of the ways it helps developers quickly create new applications is by defining a set of dependencies suitable for most users.
However, in some cases, it may be necessary to override one or more dependency versions.
In this tutorial, we’ll look at how to override Spring Boot managed dependencies and their versions.
2. Spring Boot Bill of Materials (BOM)
Let’s start by looking at how Spring Boot manages dependencies. In short, Spring Boot uses a Bill of Materials (BOM) to define dependencies and versions.
Most Spring Boot projects inherit from the spring-boot-starter-parent artifact, which itself inherits from the spring-boot-dependencies artifact. This latter artifact is the Spring Boot BOM, which is just a Maven POM file with a large dependencyManagement section:
By using Maven’s dependencyManagement, the BOM can specify default library versions should our application choose to use them. Let’s look at an example.
One of the entries in the Spring Boot BOM is as follows:
This means any artifact in the project that depends on ActiveMQ will get this version by default.
Also, notice the version is specified using a property placeholder. This is a common practice in the Spring Boot BOM, and it provides the value for this and other properties inside its own properties section.
3. Overriding Spring Boot Managed Dependency Versions
Now that we understand how Spring Boot manages dependency versions, let’s look at we can override them.
For Maven, we have two options for overriding a Spring Boot managed dependency. First, for any dependency where the Spring Boot BOM specifies the version with a property placeholder, we simply need to set that property in our project POM:
This would cause any dependency that uses the activemq.version property to use our specified version instead of the one in the Spring Boot BOM.
Additionally, if the version is specified explicitly within the dependency tag in the BOM rather than as a placeholder, then we can simply override the version explicitly in our project dependency entry:
Gradle requires a plugin to honor dependency management from the Spring Boot BOM. Therefore, to get started, we have to include the plugin and import the BOM:
apply plugin: "io.spring.dependency-management"
Now, if we want to override a specific version of a dependency, we just need to specify the corresponding property from the BOM as a Gradle ext property:
ext['activemq.version'] = '5.16.3'
And if there is no property in the BOM to override, we can always specify the version directly when we declare the dependency:
Several caveats are worth mentioning here.
For starters, it’s important to remember that Spring Boot is built and tested using the library versions specified in their BOM. Any time we specify a different library version, there is a risk we can introduce an incompatibility. Therefore it’s essential to test our applications anytime we deviate from the standard dependency versions.
Also, remember that these tips only apply when we use the Spring Boot Bill of Materials (BOM). For Maven, this means using the Spring Boot parent. And for Gradle, this means using the Spring dependencies plugin.
4. Finding Dependency Versions
We’ve seen how Spring Boot manages dependency versions and how we can override them. In this section, we’ll look at how we can find the version of a library our project is using. This is useful for identifying library versions and confirming that any overrides we apply to a project are being honored.
Maven provides a goal that we can use to display a list of all dependencies and their versions. For example, if we run the command:
We should see output similar to:
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.5.7-SNAPSHOT:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.5.7-SNAPSHOT:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.5.7-SNAPSHOT:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.5.7-SNAPSHOT:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.5.7-SNAPSHOT:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.6:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.6:compile
The output shows all artifacts and versions that are dependencies of the project. These dependencies are presented in a tree structure, making it easy to identify how every artifact is imported into the project.
In the example above, the logback-classic artifact is a dependency of the spring-boot-starter-logging library, which itself is a dependency of the spring-boot-starter module. Thus, we can navigate up the tree back to our top-level project.
Gradle provides a task that generates a similar dependency tree. For example, if we run the command:
We will get output similar to:
compileClasspath - Compile classpath for source set 'main'.
\--- org.springframework.boot:spring-boot-starter-web -> 1.3.8.RELEASE
| +--- org.springframework.boot:spring-boot:1.3.8.RELEASE
| | +--- org.springframework:spring-core:4.2.8.RELEASE
| | \--- org.springframework:spring-context:4.2.8.RELEASE
| | +--- org.springframework:spring-aop:4.2.8.RELEASE
Just like the Maven output, we can easily identify why each artifact is being pulled into the project, along with the version being used.
In the article, we have learned how Spring Boot manages dependency versions. We also saw how to override those dependency versions in both Maven and Gradle. Finally, we saw how we could verify dependency versions in both project types.