Let’s assume we have the following dependencies:-
<dependencies> <dependency> <groupId>dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.1.RELEASE</version> </dependency> </dependencies>
In this example, both dbunit
and spring-core
have a dependency on commons-logging
, but they rely on a different version:-
dbunit -> commons-logging v1.0.4 spring-core -> commons-logging v1.1.1
Based on the above configuration, what version of commons-logging
does Maven choose?
If your answer is v1.1.1, then you are absolutely right…. that you need to read the Maven documentation again.
If you are using IntelliJ, you can easily create a “Dependencies” diagram that looks like this:-
If you click on commons-logging
used by spring-core
, there is an arrow pointing to commons-logging
used by dbunit
. In another word, v1.0.4 is used.
The Maven documentation says:-
... "nearest definition" means that the version used will be the closest one to your project in the tree of dependencies, eg. if dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter ...
Since the two dependency versions for commons-logging
are at the same depth in the dependency tree, the order of the declaration becomes very important. The first declaration will be chosen and the rest will be ignored.
To proof that we are not on crack, we will swap the dependency order this time:-
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.1.RELEASE</version> </dependency> <dependency> <groupId>dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.2</version> </dependency> </dependencies>
In this case, the arrow points from commons-logging
used by dbunit
to commons-logging
used by spring-core
. In another word, v1.1.1 is used.
To avoid any further confusion on which dependency version will be chosen by Maven, you can always explicitly define that dependency in pom.xml:-
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.1.RELEASE</version> </dependency> </dependencies>
In this case, we just force both dbunit
and spring-core
to use v1.1.2:-
RECOMMENDED SOLUTION
My recommended solution is to ALWAYS define the dependencies under dependencyManagement
tag.
<dependencyManagement> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.1.RELEASE</version> </dependency> </dependencies> </dependencyManagement> <!-- Don't specify the dependency version here --> <dependencies> <dependency> <groupId>dbunit</groupId> <artifactId>dbunit</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> </dependencies>
Just because you have dependencies defined under dependencyManagement
tag, it doesn’t get included into your project classpath automatically. All it is saying is SHOULD you need to use, say dependency A, you will get version X.
If you define all your dependencies under dependencyManagement
tag, you generally don’t define the versions under dependencies
tag unless you want to override it.
In the above example, notice that you don’t need to define commons-logging
under dependencies
tag, unless you need it to compile or run your code. That said, when you run mvn clean package
on a WAR module, commons-logging
will be included in the WEB-INF/lib
.
If we generate the “Dependencies” diagram again, all dependencies on commons-logging
will now point to v1.1.2. There is zero confusion and we don’t have to worry about our peers from wrecking the project when they add new dependencies in the wrong order or upgrade existing dependency versions that may introduce potential version conflicts.
Why Should You Always Use dependencyManagement
?
Let’s assume your team works on a multi-module project that looks like this:-
myproject |- ear module <- for wrapping the war module |- pom.xml |- war module <- web application |- pom.xml |- jar module #1 <- for other teams to reuse the API |- pom.xml |- jar module #2 <- used by cron job |- pom.xml |- pom.xml <- parent pom
The main goal when working on a multi-module project is to ensure every module uses the same dependency version, if possible, to ensure there are no compatibility problems. The last thing you want is to have Team A building jar module #1 with Spring 3.x and Team B building jar module #2 with Spring 2.x.
In this situation, you should always consider defining all the dependencies under dependencyManagement
tag in the parent pom and configure all child module poms to extend the parent pom. This simple configuration enforces dependency version standardization across all modules within the project.