Maven测试依赖传递依赖覆盖编译依赖版本是否为Bug?
问题
项目存在如下依赖关系:
- compile范围依赖链:B -> C -> guava-20.0
- test范围依赖链:D -> guava-31.1
最终生成的生产Jar中却包含了guava-31.1。按预期测试依赖不应影响运行时类路径,但实际覆盖了Guava版本,这是Maven的Bug吗?
附实际场景POM示例:
Spring Eureka Client依赖guava-19.0,未添加wiremock依赖时,生产Jar中是guava-19.0;添加test scope的wiremock(依赖guava-32.1.3)后,生产Jar中的Guava版本变为32.1.3。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>mavendemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo1</name> <description>demo1</description> <properties> <java.version>17</java.version> <spring-cloud.version>2023.0.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock</artifactId> <version>3.3.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
分析与解答
这不是Maven的Bug,核心原因是Maven的依赖调解规则和Spring Boot打包插件的行为共同作用的结果:
- Maven依赖调解的全局特性
Maven在解析依赖版本时,会扫描**整个依赖树(包括test scope的依赖)**来进行版本选择,遵循"路径最近者优先",路径相同则"声明顺序优先"的规则。test scope的依赖虽然不会被加入运行时类路径,但会参与版本调解过程——它会影响最终选定的依赖版本,哪怕这个版本最终是被compile/runtime范围的依赖引用。
在你的场景中,wiremock引入的guava-32.1.3,依赖路径可能比Eureka Client的guava-19.0更短,或者在POM中声明顺序更靠后,所以Maven最终选定了guava-32.1.3作为全局版本。
- Spring Boot打包插件的行为
Spring Boot Maven插件在打包生产Jar时,只会包含compile和runtime scope的依赖,但它会使用Maven已经调解好的版本——也就是刚才选定的guava-32.1.3,而不是Eureka Client原本依赖的19.0版本。
解决方案
- 显式声明Guava版本:在POM的
<dependencies>或<dependencyManagement>中直接声明guava的指定版本,强制Maven使用该版本,覆盖依赖调解的结果:
<dependencyManagement> <dependencies> <!-- 强制指定Guava版本 --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>19.0</version> </dependency> </dependencies> </dependencyManagement>
- 排除test依赖中的Guava:在wiremock的依赖中排除guava,避免它参与依赖调解:
<dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock</artifactId> <version>3.3.1</version> <scope>test</scope> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> </exclusions> </dependency>
内容的提问来源于stack exchange,提问作者oceansize
相关产品推荐
相关产品推荐

