Maven Dependency Management
One of the core features of Maven is dependency management. When dealing with multi-module projects (which can contain hundreds or even thousands of modules or sub-projects), the dependencies between modules become very complex and difficult to manage. Maven provides a highly controlled method for handling such situations.
Transitive Dependency Resolution
A common scenario is when A depends on another library B. If another project C wants to use A, then C also needs to use library B.
Maven can avoid the need to search for all required libraries. Maven reads the project files (pom.xml) to identify dependencies between projects.
All we need to do is define the direct dependencies in the pom of each project. Maven will take care of the rest.
With transitive dependencies, the graph of included libraries can grow rapidly. When duplicate libraries are present, the potential for issues increases. Maven provides features to control the level of transitive dependencies.
Feature | Description |
---|---|
Dependency Mediation | Determines which version of a dependency will be used when multiple versions are found. If two versions have the same depth in the dependency tree, the first declared dependency will be used. |
Dependency Management | Directly specifies a manually defined version to be used. For example, if project C includes project B in its dependency management module, and B depends on A, then A can specify the version to be used when B is referenced. |
Dependency Scope | Includes dependencies that are required at various stages of the build process. |
Dependency Exclusion | Any transitive dependency can be excluded using the "exclusion" element. For example, if A depends on B, and B depends on C, then A can mark C as "excluded". |
Optional Dependency | Any transitive dependency can be marked as optional using the "optional" element. For example, if A depends on B, and B depends on C, then B can mark C as optional, so A no longer needs to use C. |
Dependency Scope
Transitive dependency resolution can be limited by using the following dependency scopes:
Scope | Description |
---|---|
Compile | This scope indicates that the dependency is available in the classpath of the project. It is the default scope. |
Provided | This scope indicates that the dependency is provided by the JDK or the web server at runtime. |
Runtime | This scope indicates that the dependency is not required for compilation but is needed for execution. |
Test | This scope indicates that the dependency is only required for test compilation and execution phases. |
System | This scope indicates that you need to provide a system path. |
Import | This scope is only used when the dependency is a pom type. It allows the current project's POM to replace specific dependencies defined in another POM. |
Dependency Management
Typically, in a common project with a series of sub-projects, we can create a pom file for common dependencies, which we call the parent pom for the sub-project poms. The following example will help you understand this concept better.
Here is the detailed explanation of the dependency graph above:
- App-UI-WAR depends on App-Core-lib and App-Data-lib.
- Root is the parent project for App-Core-lib and App-Data-lib.
- Root defines Lib1, Lib2, and Lib3 as dependencies in its dependency section.
The pom.xml file for App-UI-WAR is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.groupname</groupId>
<artifactId>App-UI-WAR</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.companyname.groupname</groupId>
<artifactId>App-Core-lib</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>com.companyname.groupname</groupId>
<artifactId>App-Data-lib</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
The pom.xml file code for App-Core-lib is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Root</artifactId>
<groupId>com.companyname.groupname</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.groupname</groupId>
<artifactId>App-Core-lib</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</project>
The pom.xml file code for App-Data-lib is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Root</artifactId>
<groupId>com.companyname.groupname</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.groupname</groupId>
<artifactId>App-Data-lib</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</project>
The pom.xml file code for Root is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.groupname</groupId>
<artifactId>Root</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>com.companyname.groupname1</groupId>
<artifactId>Lib1</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>com.companyname.groupname2</groupId>
<artifactId>Lib2</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>com.companyname.groupname3</groupId>
<artifactId>Lib3</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>
When we build the App-UI-WAR project, Maven will find all the dependencies by traversing the dependency graph and build the application.
From the above example, we can learn the following key concepts:
Common dependencies can be grouped together using the concept of a parent pom. The dependencies of the App-Data-lib and App-Core-lib projects are listed in the Root project (refer to the packaging type of Root, which is a POM).
There is no need to declare Lib1, Lib2, Lib3 as dependencies in App-UI-W. Maven handles this detail through the use of transitive dependency mechanism.