Managing Dependencies in Maven
Learn how to manage dependencies in Maven. Understand how to add dependencies in pom.xml, explore dependency scopes (compile, provided, runtime, test), and see how Maven resolves transitive dependencies.
One of the most powerful features of Apache Maven is its ability to manage project dependencies automatically.
Instead of manually downloading JAR files and placing them in your project, Maven allows you to declare them in a simple pom.xml
file, and it fetches everything you need from the Maven Central Repository.
In this lesson, you’ll learn:
- How to add dependencies to your Maven project
- Different dependency scopes and when to use them
- How Maven handles transitive dependencies
1. Adding Dependencies to pom.xml
Every Maven project has a pom.xml
file at its root. This is where you declare all the libraries (dependencies) your project needs.
Adding JUnit Dependency
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
- groupId → Identifies the organization/project (e.g.,
junit
) - artifactId → The library name (e.g.,
junit
) - version → Specific version of the library
- scope → Defines how/when this dependency is used
📌 Tip: You can search dependencies at Maven Central Repository.
2. Dependency Scopes in Maven
Maven provides different scopes to control when a dependency is available (compile time, runtime, testing, etc.).
Scope | Available At | Use Case Example |
---|---|---|
compile (default) | Compile, runtime, and test phases | Most application libraries (e.g., Spring, Hibernate) |
provided | Compile only (not packaged) | Servlet API in web apps (provided by Tomcat/Jetty) |
runtime | Runtime and test (not needed for compile) | JDBC drivers, logging frameworks |
test | Test phase only | JUnit, Mockito, TestNG |
system | Similar to provided, but requires explicit local path (not recommended) | Legacy projects with non-Maven JARs |
Example: Provided Scope (Servlet API)
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
This ensures the servlet API is available during compilation, but it won’t be included in your WAR file because the application server already provides it.
3. Transitive Dependencies
Maven not only downloads the dependencies you explicitly declare but also their transitive dependencies (the libraries your libraries depend on).
Example: Spring Boot Starter Web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.0</version>
</dependency>
Adding just this one dependency pulls in:
- Spring Web MVC
- Jackson (for JSON processing)
- Tomcat (as the embedded servlet container)
- Logging frameworks
This saves you time because you don’t need to manually add every single dependency.
4. Handling Dependency Conflicts
Sometimes, transitive dependencies may include different versions of the same library.
To check this, use:
mvn dependency:tree
If there’s a version conflict, you can override it in your pom.xml
by explicitly declaring the desired version:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
Key Takeaways
- Dependencies are declared inside the
<dependencies>
section ofpom.xml
. - Use scopes (
compile
,provided
,runtime
,test
) to control when dependencies are available. - Maven automatically manages transitive dependencies, but you should monitor for conflicts.
- The
mvn dependency:tree
command helps debug and resolve version issues.