maven依赖冲突本质是同一库不同版本被多路径引入,jvm只能加载一个版本,导致运行时nosuchmethoderror或classnotfoundexception;其根源在于依赖传递机制和“最短路径优先+声明优先”的仲裁规则,需用mvn dependency:tree -dverbose定位全量冲突路径。

当多个依赖路径引入同一库的不同版本时,Maven会按规则选择一个版本加载,但被丢弃的版本可能正被某处代码调用——这时编译能过,运行时报NoSuchMethodError或ClassNotFoundException,问题根源就藏在依赖传递机制里。
依赖传递自动拉入间接依赖
你在pom.xml中只写了<dependency><groupid>org.apache.httpcomponents</groupid><artifactid>httpclient</artifactid><version>4.5.13</version></dependency>,Maven却悄悄把commons-codec:1.11和commons-logging:1.2也加进来了。
这是因为httpclient自身pom声明了对这两个库的compile范围依赖——只要scope不是provided或test,就会向下传递。
你没写它们,但它们已进入你的classpath。
不同路径引入同一坐标不同版本
假设项目直接依赖A库和B库:
A库→C库→guava:31.1-jre
B库→D库→guava:29.0-jre
此时Maven依赖树里出现两个guava版本,但JVM类加载器只能加载一个com.google.common.base.Preconditions类。
【Maven不会同时保留两个版本】——它必须选一个,而选择依据是路径长度和声明顺序,不是API兼容性。
冲突爆发的典型信号
方法一:运行时报NoSuchMethodError: com.google.common.collect.ImmutableList.of(Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;
原因:代码调用的是guava 31.1新增的静态工厂方法,但Maven选了路径更短的29.0版本,该版本根本没有这个方法签名。
方法二:启动报ClassNotFoundException: org.springframework.boot.web.servlet.FilterRegistrationBean
原因:Spring Boot 2.7.x中该类已移至spring-boot-autoconfigure,但某个老版starter仍试图从spring-boot里加载——而Maven因路径优先选了旧版spring-boot jar,导致类缺失。
验证冲突是否存在
第一步:执行mvn dependency:tree -Dverbose
第二步:在输出中搜索目标坐标,例如grep -A 5 'guava'
第三步:观察所有匹配行的缩进层级和版本号,找出路径长度不同的分支
注意:-Dverbose参数必须加上,否则被仲裁掉的版本不会显示——你看到的只是“幸存者”,不是全部真相。











