One of the most common things you’ll do when developing a a modern Java application is add libraries and frameworks. You usually declare them as dependencies. But these dependencies can import other dependencies, too… so how do you know which dependencies your app is using, without going crazy?
Well, as it turns out, Maven can come to our rescue.
In Maven, the
dependency:tree command will list all dependencies for a Java project.
But first, why?
Apps can get complicated really quickly.
If you’re building or supporting a big Java application, your app is probably using a bunch of libraries.
And those libraries use other libraries!
An app always starts out clean and simple, but it can become tiresome very quickly. Especially if you need to upgrade your app or resolve conflicts between the libraries you’re using.
(This is one of the problems that Spring Boot tries to resolve.)
dependency:tree can help you manage your libraries, because it allows you to do things like this:
Find out which dependencies you’re pulling in
Find out which dependency is pulling in another dependency
Resolve version conflicts between different dependencies
Find out the exact versions of dependencies that you’re using in a project – this is really useful when you need to check if you’re exposed to a security vulnerability (e.g. the Log4Shell vulnerability)
Let’s see how it works.
List all dependencies
First - how do you list all of the dependencies that your Java application is using?
Perhaps you need to produce a report for someone?
It’s OK! We can find this information easily with Maven.
One of Maven’s main jobs is to handle your Java project’s dependencies. So when Maven runs, it builds a “tree” of all dependencies in your project.
If you tell Maven to print a dependency tree, you will be able to see all of the dependencies in your project.
To do this:
Drop to a terminal.
Change directory to your Java project (the location of your
This prints the dependency tree.
It’s a text report which shows all of the dependencies in your project, both the direct and transitive dependencies.
If you’re not sure what the terms ‘direct’ and ‘transitive’ mean, then scroll further down for a quick guide to dependencies and how they work in Maven!
Here’s an example of what the output looks like:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ maven-dependency-tree-demo --- [INFO] org.example:maven-dependency-tree-demo:jar:1.0-SNAPSHOT [INFO] +- org.apache.logging.log4j:log4j-core:jar:2.17.2:compile [INFO] | \- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile
How to read the output
The output looks a bit like gobbledegook but it’s actually telling us some important relationships between our project’s dependencies:
So now we’ve got a list of the dependencies in our project. What if we want to track down the parent of a particular dependency?
Find the origin of a dependency
You can also use the
dependency:tree command to find why a certain dependency was imported.
If you’ve not declared a dependency in your pom.xml, but it’s appearing in your project, it’s being imported somewhere as a transitive dependency.
You can see exactly which dependency is importing it, by looking at the
dependency:tree report, and walking back up the “tree” to find its parent.
It’s a visual task, but we can make it a lot easier by filtering the report.
-Dincludes and give the name (
artifactId) or the
group of the dependency we’re looking for.
Let’s see an example…
Example: look for a specific artifact
For example, we want to look for a specific artifact from the
org.apache.logging.log4j group called
-Dincludes, give the group name, then a colon
: and then the artifact name:
mvn dependency:tree -Dincludes=org.apache.logging.log4j:log4j-api
The output tells us that we’re pulling in
log4j-api (the bottom line) as a transitive dependency of
log4j-core (the second-to-last line):
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ maven-dependency-tree-demo --- [INFO] org.example:maven-dependency-tree-demo:jar:1.0-SNAPSHOT [INFO] \- org.apache.logging.log4j:log4j-core:jar:2.17.2:compile [INFO] \- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile
Now you know where
log4j-api is coming from.
This is all very good if you love the Terminal, but what if you want to do this in your Java IDE?
View Maven dependency tree in IntelliJ IDEA
If you’re a Java developer, you’re probably using an IDE to write code and manage your project.
In IntelliJ IDEA (which we think is the best Java IDE!), you can check out your project’s Maven dependencies without needing to drop to a Terminal.
You can get this information in the Maven Tool Window.
First go to View → Tool Windows → Maven, to make sure that the Maven window is visible.
Then, you can see the tree of dependencies:
The top-level elements in the tree are your direct dependencies, and the child elements are the transitive dependencies.
How Maven dependencies work
If you’re new to Maven and Java development, some of these terms might be bit unfamiliar to you.
So here’s a quick “blagger’s guide” to Maven dependencies:
What is a dependency?
A dependency is some code which your project depends on. It could be a library, a framework, or even just a single class.
In Java, a dependency is normally packaged into a “.jar” file. (A jar file is basically a zip file which contains compiled Java classes, and some configuration files.)
In Maven, you declare your project’s dependencies in your
You give the
version of the dependency you want to pull in:
<dependencies> <!-- Declaring one dependency --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.17.2</version> </dependency> <!-- More dependencies could go here --> </dependencies>
Once you’ve added the dependency into your POM, Maven does the rest. At build time, it downloads the dependency, and makes it available to the build process.
What’s the difference between direct and transitive dependencies?
It’s probably good to understand that in computer programming, there are two types of dependencies, direct and transitive.
A direct dependency is one that you declare yourself:
→ e.g. “I want to use the Vert.x library”.
When a dependency you’re using declares a dependency on another project, it’s called a transitive dependency.
→ e.g. “I want to use Vert.x, but it depends on the Jackson library.” (Jackson is the transitive dependency here)
How the dependency tree works
Each direct dependency can declare its own transitive dependencies. It declares this information in its POM file.
And then those transitive dependencies might have their own transitive dependencies, too.
Once your Java program is big enough, your dependencies will start to look like a tree structure.
And that’s exactly what Maven’s
dependency:tree command tries to show for you.
So when you’re developing Java programs, we use a build tool (like Maven) to declare our direct dependencies, and the build tool sorts out all of the transitive dependencies for us (that’s what Maven does best!)
The mysteries of Maven
Maven can be quite complicated and, well, mysterious…..
…but it’s full of lots of useful little features like
dependency:tree, which is helpful.
So now you can list dependencies in your project, you’ll have better understanding about the libraries you’re actually using.
And you’ll have a bit more peace of mind, in case you need to resolve dependency conflicts or security alerts.
Comments? Share your Maven tips with the community below!
Keep on drinking that hot Java. ☕