Maven Dependency Management
Master Maven dependency management including dependency declaration, scope management, version resolution, BOMs, and dependency tree optimization.
Overview
Maven's dependency management is a cornerstone of Java project build systems. It handles transitive dependencies, version conflicts, and provides mechanisms for controlling dependency resolution across multi-module projects.
Dependency Declaration
Basic Dependency
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.2.0</version> </dependency>
Dependency with Scope
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.1</version> <scope>test</scope> </dependency>
Optional Dependencies
<dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> <version>3.0.2</version> <optional>true</optional> </dependency>
Dependency Scopes
Available Scopes
Scope Compile CP Test CP Runtime CP Transitive
compile Yes Yes Yes Yes
provided Yes Yes No No
runtime No Yes Yes Yes
test No Yes No No
system Yes Yes No No
import N/A N/A N/A N/A
Scope Examples
<!-- Compile scope (default) - available everywhere --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.14.0</version> </dependency>
<!-- Provided - available at compile, not packaged --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency>
<!-- Runtime - only needed at runtime --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.1</version> <scope>runtime</scope> </dependency>
<!-- Test - only for testing --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.8.0</version> <scope>test</scope> </dependency>
Version Management
Property-Based Versions
<properties> <spring-boot.version>3.2.0</spring-boot.version> <junit.version>5.10.1</junit.version> <jackson.version>2.16.0</jackson.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> </dependencies>
Version Ranges
<!-- Exact version --> <version>1.0.0</version>
<!-- Greater than or equal --> <version>[1.0.0,)</version>
<!-- Less than --> <version>(,1.0.0)</version>
<!-- Range inclusive --> <version>[1.0.0,2.0.0]</version>
<!-- Range exclusive --> <version>(1.0.0,2.0.0)</version>
Latest Version (Not Recommended)
<!-- Avoid in production --> <version>LATEST</version> <version>RELEASE</version>
Dependency Management Section
Centralizing Versions
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson</groupId> <artifactId>jackson-bom</artifactId> <version>2.16.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<!-- No version needed when declared in dependencyManagement --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies>
BOM (Bill of Materials) Import
<dependencyManagement> <dependencies> <!-- Spring Boot BOM --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.0</version> <type>pom</type> <scope>import</scope> </dependency>
<!-- AWS SDK BOM -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.23.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- JUnit BOM -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.10.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Exclusions
Excluding Transitive Dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
<!-- Add alternative --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
Excluding Logging Frameworks
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
Dependency Analysis
View Dependency Tree
Full dependency tree
mvn dependency:tree
Filter by artifact
mvn dependency:tree -Dincludes=org.slf4j
Output to file
mvn dependency:tree -DoutputFile=deps.txt
Verbose output showing conflict resolution
mvn dependency:tree -Dverbose
Analyze Dependencies
Find unused declared and used undeclared dependencies
mvn dependency:analyze
Show only problems
mvn dependency:analyze-only
Include test scope
mvn dependency:analyze -DignoreNonCompile=false
List Dependencies
List all dependencies
mvn dependency:list
List with scope
mvn dependency:list -DincludeScope=runtime
Conflict Resolution
Maven's Default Strategy
Maven uses "nearest definition wins" for version conflicts:
A -> B -> C 1.0 A -> C 2.0
Result: C 2.0 is used (nearest to root)
Forcing Versions
<dependencyManagement> <dependencies> <!-- Force specific version across all modules --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.9</version> </dependency> </dependencies> </dependencyManagement>
Enforcer Plugin for Version Control
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <id>enforce</id> <goals> <goal>enforce</goal> </goals> <configuration> <rules> <dependencyConvergence/> <requireUpperBoundDeps/> <banDuplicatePomDependencyVersions/> </rules> </configuration> </execution> </executions> </plugin> </plugins> </build>
Multi-Module Projects
Parent POM Dependency Management
<!-- parent/pom.xml --> <project> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0</version> <packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<!-- module/pom.xml --> <project> <parent> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0</version> </parent>
<artifactId>module</artifactId>
<dependencies>
<!-- Version inherited from parent -->
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
</project>
Repository Configuration
Central Repository
<repositories> <repository> <id>central</id> <url>https://repo.maven.apache.org/maven2</url> </repository> </repositories>
Private Repository
<repositories> <repository> <id>company-repo</id> <url>https://nexus.company.com/repository/maven-public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories>
Repository in Settings.xml
<!-- ~/.m2/settings.xml --> <settings> <servers> <server> <id>company-repo</id> <username>${env.REPO_USER}</username> <password>${env.REPO_PASS}</password> </server> </servers> </settings>
Best Practices
-
Use dependencyManagement - Centralize versions in parent POMs
-
Import BOMs - Use well-maintained BOMs for framework dependencies
-
Avoid Version Ranges - Pin exact versions for reproducibility
-
Regular Updates - Keep dependencies current for security
-
Minimize Scopes - Use appropriate scopes to reduce package size
-
Exclude Unused - Remove unused transitive dependencies
-
Document Exclusions - Comment why exclusions are needed
-
Run dependency:analyze - Regularly check for issues
-
Use Enforcer Plugin - Ensure dependency convergence
-
Lock Versions - Use versions-maven-plugin for updates
Common Pitfalls
-
Version Conflicts - Transitive dependency version mismatches
-
Missing Exclusions - Duplicate classes from different artifacts
-
Wrong Scope - Compile vs runtime vs provided confusion
-
Outdated Dependencies - Security vulnerabilities in old versions
-
Circular Dependencies - Module A depends on B depends on A
-
Snapshot in Production - Using SNAPSHOT versions in releases
-
System Scope - Hardcoded paths break portability
-
Optional Misuse - Marking required dependencies as optional
Troubleshooting
Debug Dependency Resolution
Enable debug output
mvn dependency:tree -X
Show conflict resolution
mvn dependency:tree -Dverbose=true
Force Re-download
Clear local repository cache
mvn dependency:purge-local-repository
Force update
mvn -U clean install
Check Effective POM
See resolved dependency versions
mvn help:effective-pom
When to Use This Skill
-
Adding new dependencies to a project
-
Resolving version conflicts
-
Setting up multi-module project dependencies
-
Configuring BOM imports
-
Optimizing dependency trees
-
Troubleshooting classpath issues
-
Upgrading dependency versions
-
Excluding problematic transitive dependencies