Listen to this story
Developer Advocate, eternal learner, author
Recently, my good friend Richard Fichtner advised using the mvn dependency:analyze
command to get rid of declared but unused dependencies:
While it was a great idea years ago, it's dangerous advice today. In this post, I'd like to explain what the plugin does and why you shouldn't use it but in the most straightforward projects.
mvn dependency:analyze
commandMaven uses a plugin architecture; in the above command, the plugin is maven-dependency-plugin. A plugin hosts several related goals. Here, it's analyze
.
Analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared. This goal is intended to be used standalone, thus it always executes the
test-compile
phase - use thedependency:analyze-only
goal instead when participating in the build lifecycle.
By default,
maven-dependency-analyzer
is used to perform the analysis, with limitations due to the fact that it works at bytecode level, but any analyzer can be plugged in throughanalyzer
parameter.
maven-dependency-analyzer
is a shared Maven component. Its description is quite descriptive:
Analyzes the dependencies of a project for undeclared or unused artifacts.
Warning: Because analysis is done on the bytecode rather than the source, some cases are not detected including constants, annotations with source-only retention, and links in Javadoc. This can lead to incorrect results when these are the only uses of a dependency.
The main component is
ProjectDependencyAnalyzer
, which usesClassAnalyzer
andDependencyAnalyzer
.
The warning clearly shows that it works at the bytecode level. In particular, it explicitly mentions that it doesn't consider source-level annotations.
I described how to design your own Spring Boot starter a long time ago, and it didn't change a lot since then. If you're new to Spring Boot starters, here's a summary.
SpringBoot relies on AutoConfiguration classes. AutoConfiguration classes are regular configuration classes, i.e., they contribute to the application classes. You can set specific activation criteria, such as the presence of a Spring property, but these are not specific to auto-configuration.
Here's a very simplified flow:
Simplified sequence diagram of the auto-configuration in Spring Boot
The JAR that automatically comes with Spring Boot is org.springframework.boot:spring-boot-autoconfigure
. You can check the content of its META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
:
...
org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebSessionIdResolverAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
As an example, here's the RestClientAutoConfiguration
:
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class }) //1
@ConditionalOnClass(RestTemplate.class) //2
@Conditional(NotReactiveWebApplicationCondition.class) //3
public class RestTemplateAutoConfiguration {
// Class body
}
RestTemplate
class is on the classpath
Note that the class loader loads the RestTemplateAutoConfiguration
class just fine, regardless of whether the RestTemplate
class is on the classpath or not! Spring leverages this mechanism to its fullest, as seen above. In effect, the resolution of classes configured in annotations is deferred until they are explicitly accessed.
maven-dependency-analyzer
into the modern ageCommitters designed the analyzer in 2007: here's how it looked like then. Spring Boot started later, in 2010. For this reason, the analyzer didn't take deferred class loading in annotations. Note that this is still not the case; the project doesn't get a lot of love.
When using the plugin on a Spring Boot project, you'll get a lot of false positives. I tried it with a simple Spring Boot project, using WebFlux and R2DBC on PostgreSQL.
Here's a slight excerpt of the output when I run mvn analyze:dependencies
:
[WARNING] Unused declared dependencies found:
[WARNING] org.springframework.boot:spring-boot-starter-data-r2dbc:jar:3.4.0:compile
[WARNING] org.testcontainers:postgresql:jar:1.20.4:test
[WARNING] org.testcontainers:r2dbc:jar:1.20.4:test
If I remove any of these dependencies, tests don't run.
What would be necessary to make the analyzer work with Spring Boot projects? Let's analyze the analyzer.
Analyzer class diagram
The plugin allows configuring another analyzer:
Specify the project dependency analyzer to use (plexus component role-hint). By default, maven-dependency-analyzer is used. To use this, you must declare a dependency for this plugin that contains the code for the analyzer. The analyzer must have a declared Plexus role name, and you specify the role name here.
Type:
java.lang.String
Since:
2.2
Required:
No
User Property:
analyzer
Default:
default
We can create an overall analyzer that reuses the above but adds one specific to Spring Boot.
The current state of the Maven analyzer doesn't offer any benefit to modern Spring Boot projects. The existing code is open to configuration and even extension. However, we would need to embed a lot of Spring Boot logic. For Quarkus and Micronaut projects, we would require dedicated code as well.
I don't know if it's worth the time and effort. If you think it is, I hope this blog post can serve as an early-stage analysis.
To go further:
Originally published at A Java Geek on March 9th, 2025