| # | Section |
|---|---|
| 1 | What is a dependency? |
| 2 | Declare a dependency |
| 3 | Scopes |
| 4 | Repositories |
| 5 | Transitive resolution |
| 6 | Diagnose and exclude |
| 7 | Quiz — Dependency management |
| 8 | Practice — Add dependencies |
| 9 | Summary |
A dependency is an external library your project needs in order to compile or run. Instead of rewriting code (JSON parsing, database access, tests…), you reuse proven libraries.
Maven manages these dependencies automatically: you declare them in the pom.xml, and Maven downloads them and puts them on the classpath.
| Without Maven | With Maven |
|---|---|
Download each .jar by hand | Declare 3 lines in pom.xml |
| Manage the classpath manually | Maven builds it automatically |
| Find compatible libraries | Automatic transitive resolution |
A dependency is also identified by its GAV coordinates (
groupId:artifactId:version), exactly like your own project. That is how Maven knows what to download.
Dependencies are declared in the <dependencies> block of the pom.xml. Each dependency is a <dependency> tag with its GAV coordinates.
<dependencies>
<!-- Google's Gson library for working with JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>To find a library's correct coordinates, you consult Maven Central (search.maven.org), which provides the XML block ready to copy.
# After adding a dependency, trigger the download
mvn compile
# Or download explicitly without compiling
mvn dependency:resolve| Element | Role |
|---|---|
<groupId> | The organization that publishes the library |
<artifactId> | The library name |
<version> | The desired version |
Maven caches libraries in
~/.m2/repository. A library that is already downloaded is never downloaded again: that is why later builds are fast.
🔧 Mini-exercise — Declare the Gson dependency (com.google.code.gson:gson:2.10.1) in a <dependency> block.
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>A dependency's scope indicates when and where it is available: at compile time, at test time, at runtime, or provided by the environment. You specify it with <scope>.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope> <!-- available only for tests -->
</dependency>| Scope | Available at compile | Available at test | Available at runtime | Included in the artifact |
|---|---|---|---|---|
compile (default) | ✅ | ✅ | ✅ | ✅ |
test | ❌ | ✅ | ❌ | ❌ |
provided | ✅ | ✅ | ❌ (provided by the server) | ❌ |
runtime | ❌ | ✅ | ✅ | ✅ |
Concrete examples:
| Library | Typical scope | Why |
|---|---|---|
| JUnit | test | Only useful for running tests |
| Servlet API | provided | The server (Tomcat) already provides it |
| JDBC driver | runtime | Required at runtime, not at compile time |
| Gson | compile | Used everywhere in the code |
Putting JUnit in
compileinstead oftestis a common mistake: it unnecessarily ships the test library in your final deliverable. Always choose the most restrictive scope that still fits.
🔧 Mini-exercise — Declare a JUnit Jupiter (5.10.2) dependency with test scope in the pom.xml.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>Maven downloads dependencies from repositories. There are three levels:
| Repository type | Description |
|---|---|
| Local repository | Cache on your machine: ~/.m2/repository |
| Maven Central | The worldwide public repository, default source |
| Private repository | Company server (Nexus, Artifactory) for internal libraries |
To add a private repository, you declare it in the pom.xml:
<repositories>
<repository>
<id>nexus-entreprise</id>
<url>https://nexus.monentreprise.com/repository/maven-public/</url>
</repository>
</repositories># Force dependency updates from the repositories
mvn clean install -U
# Clear a library's cache to force a re-download
# (manually delete the corresponding folder in ~/.m2)| Case | Repository used |
|---|---|
| Public open-source library | Maven Central |
| In-house company library | Private repository (Nexus/Artifactory) |
| Library already downloaded | Local cache ~/.m2 |
Companies often use a private repository as a mirror of Maven Central: that speeds up downloads and lets them control which libraries are allowed.
A dependency can itself depend on other libraries: those are transitive dependencies. Maven downloads them automatically — you do not have to list them.
You declare one dependency, Maven resolves dozens:
<!-- A single declaration... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.5</version>
</dependency>
<!-- ...automatically brings in spring-web, jackson, tomcat, etc. -->To visualize the complete tree:
# Displays the dependency tree (direct + transitive)
mvn dependency:treeTypical output:
com.exemple:mon-app:jar:1.0.0
+- org.springframework.boot:spring-boot-starter-web:jar:3.2.5:compile
| +- org.springframework:spring-web:jar:6.1.6:compile
| +- com.fasterxml.jackson.core:jackson-databind:jar:2.15.4:compile
| \- org.apache.tomcat.embed:tomcat-embed-core:jar:10.1.20:compileWhen two paths bring two different versions of the same library, Maven applies the “nearest in the tree” rule (nearest wins): the version closest to your project wins.
Transitive resolution is one of Maven's greatest strengths: you declare high-level intentions, and Maven assembles the whole dependency puzzle for you.
🔧 Mini-exercise — Write the command that displays the complete dependency tree (direct and transitive).
mvn dependency:treeSometimes a transitive dependency causes trouble (version conflict, unwanted library). Maven provides tools to diagnose and fix that.
To exclude an unwanted transitive dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.5</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>Useful diagnostic commands:
| Command | Role |
|---|---|
mvn dependency:tree | Displays the complete dependency tree |
mvn dependency:analyze | Detects unused or missing dependencies |
mvn dependency:resolve | Downloads all dependencies |
# Spot declared-but-unused dependencies,
# and used-but-undeclared ones
mvn dependency:analyzeBefore excluding anything, run
mvn dependency:treeto understand where the problematic dependency comes from. You never exclude blindly.
🔧 Mini-exercise — Write the command that spots declared-but-unused dependencies (and the reverse).
mvn dependency:analyzeQuestion 1: Where does Maven cache downloaded dependencies?
a) In target/
b) In ~/.m2/repository
c) In src/main/resources
d) On the web server
Answer: b) — The local repository ~/.m2/repository acts as a cache: a library that is already downloaded is not downloaded again.
Question 2: Which scope fits JUnit, a library used only for tests?
a) compile
b) runtime
c) test
d) provided
Answer: c) — The test scope makes the dependency available to tests only and excludes it from the final deliverable.
Question 3: What are transitive dependencies?
a) Dependencies you must list manually
b) The dependencies of your dependencies, resolved automatically by Maven
c) Temporary dependencies
d) Test dependencies
Answer: b) — A declared dependency brings its own dependencies; Maven downloads them automatically.
Question 4: Which command displays the complete dependency tree?
a) mvn list
b) mvn dependency:tree
c) mvn show-deps
d) mvn package
Answer: b) — mvn dependency:tree shows direct and transitive dependencies, useful for diagnosing conflicts.
Question 5: Which scope should you choose for the Servlet API, provided by the Tomcat server?
a) compile
b) test
c) provided
d) runtime
Answer: c) — provided: the library is needed to compile, but the runtime environment (the server) already provides it.
In an existing project, add two dependencies: Gson (com.google.code.gson:gson:2.10.1) with compile scope to work with JSON, and JUnit Jupiter (org.junit.jupiter:junit-jupiter:5.10.2) with test scope. Then inspect the dependency tree.
<dependencies> block<dependencies>
<!-- Gson: used everywhere in the code (compile scope by default) -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- JUnit: tests only -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>Verification:
# Download and display the dependency tree
mvn dependency:treeExpected result:
com.exemple:mon-app:jar:1.0.0-SNAPSHOT
+- com.google.code.gson:gson:jar:2.10.1:compile
\- org.junit.jupiter:junit-jupiter:jar:5.10.2:test
+- org.junit.jupiter:junit-jupiter-api:jar:5.10.2:test
+- org.junit.jupiter:junit-jupiter-params:jar:5.10.2:test
\- org.junit.jupiter:junit-jupiter-engine:jar:5.10.2:testNotice that
junit-jupiterautomatically brings in-api,-params, and-engine: those are its transitive dependencies. You declared only one line; Maven resolved the rest.
<dependencies>; Maven downloads it and caches it in ~/.m2.compile, test, provided, runtime) control when the dependency is available.~/.m2), Maven Central (public), private (Nexus/Artifactory).mvn dependency:tree lets you inspect it.Lesson 04 — Lifecycle and phases: understand the phases (compile, test, package, install, deploy) that orchestrate the build.
All rights reserved. Any reproduction, distribution, use or adaptation of this course, in whole or in part, is strictly prohibited without the prior written authorization of Dr. Haythem REHOUMA.
Course created by Dr. Haythem REHOUMA — Development and Deployment of Data Solutions