Lately while working with Spring WebFlux, I came across this really helpful concept of the bill of materials also known as BOM, a concept not really limited to Spring at all. BOM is one of the few ways Spring helps us forget about issues related to transitive dependencies and focus solely on our project requirements.
So when we generally create a large scale project with dozens or hundreds of dependencies, chances are multiple of them use something common internally by transitive dependencies. So let say we have a dependency called “common-3.0.0.RELEASE” which is used by our logging artifact and “common-2.9.0.Release” used by our testing framework. In such a case we have a conflict. We need to ensure we get a working version so that both the artifacts can work together in synergy.
There’s a way Maven solves this problem for us. It uses the concept of dependency mediation. It works on something known as a path to the dependency. Whoever has the shortest path to the project is picked over the other. So let say in our case the chain is something like log4j -> commons-2.9.0.Release and JUnit -> unit-tests -> common.3.0.0.Release, maven selects commons 2.9 since it has a shorter path to this POM.
But why rely on some external method when we can control stuff right? We can always use a version we’re hoping to use even transitively by specifying it in the POM. Sure, but this would clutter our POM because how many are we going to mention? Or we could do one better. We can let dependency management take control.
Maven let us define the versions of our dependencies or transitive dependencies in a separate pom. It is in this pom that we declare the versions and scope of the dependencies. Hence we get a centralized place to mention all the dependency details.
Let’s create a sample bill of materials pom file.
As can be seen, the bill of materials is a perfectly normal POM file where we can include our dependency information.
How to use it?
Now we understand that there’s a file which contains all information regarding the versions of any dependency that we may want to be a part of our project. Next thing is to include this file in our original POM.
One way to do this is the way Spring does it, by inheriting it like a parent project. All we need to do to inherit is to provide the parent information in the parent tags.
Though this is how spring works, we can not inherit from more than one parent pom. So in case you wish to inherit from more than one, we pass the credentials in our dependencies.
So there are multiple ways of deciding the versions, which means there is an order of precedence.
- Versions provided in the direct declaration in POM take the highest precedence.
- Version provided in parent pom takes second precedence.
- Version from imported pom takes third place
- Lastly, whatever we get from dependency mediation
In case of conflict when inheriting multiple poms, the dependency declared earlier in order takes precedence.
So now you can understand why things magically auto-configure in spring poms, what a BOM is and what to do in case there’s any custom operation you wish to perform. Thanks for reading the blog and please let me know your comments on the same.