When you're writing Java applications, chances are you're using Maven for dependency management. It lets you declare the artifacts you need to build your application. Those artifacts also depend on other artifacts. This means you have transitive dependencies - dependencies you didn't declare yourself but you need them anyway.
As you add more and more dependencies, you might all of a sudden see artifacts that surprise you. For example, you're buidling a Spring Boot app and you notice that you have both Log4J and SLF4J. Why on earth does my application have two logging libraries?
You can ask Maven to print the whole tree of direct and transitive dependencies.
The command is
mvn dependency:tree, but it's output can be large.
it.mulders.brainfuck-jvm:demo-app:jar:0.1-SNAPSHOT +- org.springframework.boot:spring-boot-starter-thymeleaf:jar:2.1.8.RELEASE:compile | +- org.thymeleaf:thymeleaf-spring5:jar:3.0.11.RELEASE:compile | | +- org.thymeleaf:thymeleaf:jar:3.0.11.RELEASE:compile | | | +- org.attoparser:attoparser:jar:2.0.5.RELEASE:compile | | | \- org.unbescape:unbescape:jar:1.1.6.RELEASE:compile | | \- org.slf4j:slf4j-api:jar:1.7.28:compile | \- org.thymeleaf.extras:thymeleaf-extras-java8time:jar:3.0.4.RELEASE:compile ... omitted for brevity ... \- org.springframework.boot:spring-boot-starter-test:jar:2.1.8.RELEASE:test +- org.springframework:spring-test:jar:5.1.9.RELEASE:test \- org.xmlunit:xmlunit-core:jar:2.6.3:test
In the case of Spring (Boot), even a simple application pulls in around 70 other dependencies. How to find out which of them causes Log4J or SLF4J to be included as well?
-Dexcludes=..., which removes certain artifacts from the output.
-Dincludes=..., which removes certain artifacts from the output.
For example, if we don't want to see ByteBuddy dependencies in the output, we use
mvn dependency:tree -Dexcludes=net.bytebuddy.
Both parameters accept a comma-separated list of artifacts that must (or must not) be included in the output.
This means we can also ask the plugin to answer the original question with
mvn dependency:tree -Dincludes=*:slf4j-api,*:log4j-api:
it.mulders.brainfuck-jvm:demo-app:jar:0.1-SNAPSHOT +- org.springframework.boot:spring-boot-starter-web:jar:2.1.8.RELEASE:compile | \- org.springframework.boot:spring-boot-starter:jar:2.1.8.RELEASE:compile | \- org.springframework.boot:spring-boot-starter-logging:jar:2.1.8.RELEASE:compile | \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.2:compile | \- org.apache.logging.log4j:log4j-api:jar:2.11.2:compile \- org.springframework.boot:spring-boot-starter-thymeleaf:jar:2.1.8.RELEASE:compile \- org.thymeleaf:thymeleaf-spring5:jar:3.0.11.RELEASE:compile \- org.slf4j:slf4j-api:jar:1.7.28:compile
Excellent! The output is much shorter, and it contains only the information needed to answer the question.