在项目开发中,多少都会遇到Maven
的jar包冲突问题,如:
- 引入新包后报了
NoSuchMethodError
- 很多个依赖都引入了不同版本的jar包A ,造成A的版本不统一。
要解决jar包冲突的问题,就需要先搞清楚Maven
中的依赖传递原则 以及造成jar包冲突背后的原理。
Maven中的依赖传递原则
Maven中的依赖传递原则为:最短路径优先原则,最先声明优先原则。
1. 最短路径优先原则
举例:假设工程引入了jar包A 和B ,它们都依赖了Z这个jar包:
A --> X --> Y --> Z(2.5)
B --> X --> Z(2.0)
则最终Z(2.0) 版本生效,因为它的路径更短。
2. 最先声明优先原则
举例:假设工程引入了jar包A 和B ,它们都依赖了Z这个jar包,在工程pom.xml文件中,A先于B声明:
A --> Z(2.5)
B --> Z(2.0)
则最终Z(2.5) 版本生效,因为这里A最先声明,所以传递过来的Z选用2.5版本。
Maven中Jar包冲突原理
根据上面的举例,假设工程中引入了jar包A 和B ,它们都依赖了Z这个jar包:
A --> X --> Y --> Z(2.5)
B --> X --> Z(2.0)
根据最短路径优先原则,最终依赖的是Z(2.0) 版本生效。
如果在Y 包中使用了Z包2.5版本中新的method ,当运行到这段逻辑时,就会报NoSuchMethodError
。
原因是: Y 包本来依赖Z的2.5版本,但因为jar包冲突,Maven根据最短路径优先原则选择了Z的2.0版本 ,而Z的2.0版本中没有这个新的method,导致出错。
注意: 不是所有冲突都会引起运行异常,只有高版本jar包向下不兼容,或者新增了某些低版本没有的API,则有可能导致冲突出错。
Maven冲突检测
1. 在IDEA安装Maven Helper
插件
2. 使用Maven Helper
插件进行jar包冲突检测
1)通过全部依赖树或列表,标亮的即存在jar包冲突,选中则可以查看jar包冲突具体版本
上图示例,表明log4j 这个jar包,有2个传递依赖,分别为1.2.14版本 和1.2.16版本,冲突描述为:
omitted for conflict with 1.2.14. --- 由于与1.2.14版本冲突而被省略
2)搜索jar包检测冲突
3)通过mvn命令检测冲突
命令为:mvn dependency:tree -Dverbose
注意:不要省略
-Dverbose
参数,否则不会显示被忽略的包
解决冲突
1. 排除法
选中冲突的jar包,点击右键,选择Exclude,即可排查掉此jar包
2. 版本锁定法
如果很多个依赖都传递了jar包A ,涉及了很多个版本,但只想指定一个版本,用排除法一个个去exclude
太麻烦。
用版本锁定法: 公司项目一般都会有父级pom文件,想指定哪个版本,只需在项目的父级pom中定义即可,其他子module的pom中,只引入依赖,不需要指定版本。
举例:
在项目父级pom中定义要依赖的mapstruct
版本,如下:
xml
<!-- 父级pom文件 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.0.Final</version>
</dependency>
</dependencies>
</dependencyManagement>
在其他子module的pom中,只引入依赖,不需要再指定mapstruct
的版本,如下:
xml
<!-- 子module的pom文件 -->
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
</dependencies>
注意: 版本锁定并不排除jar包,而是显示上把所有版本不一致的jar包变成统一一个版本。
总结
- 尽量在父级pom中定义
<dependencyManagement>
来管理和统一依赖版本。 - 对外提供的jar包,尽量不要传递依赖不必要的jar包。
- 使用maven命令来检测分析依赖:
- 使用
mvn dependency:tree -Dverbose
命令来检测jar包冲突。 - 使用
mvn dependency:analyze-only
命令来检测声明了但没有被使用的依赖,尽量去掉。 - 使用
mvn dependency:analyze-duplicate
命令来分析重复定义的依赖,清理掉重复定义的依赖。
- 使用