一:依赖冲突的表现
- 例1:当服务启动或者调用发生如下异常时,NoSuchMethodError,ClassNotFoundException 等。则一般是因为jar包冲突引起的,如最近项目中遇到的一个问题,就是因为com.alipay.common包发生了版本冲突,项目中实际编译打包的com.alipay.common:tracer包中RpcLogContext类没有getConnEstablishedSpan()方法引起的,虽然本地编译没有问题但服务跑起来就会报NoSuchMethodError找不到方法错误
java.lang.NoSuchMethodError: com.alipay.common.tracer.context.RpcLogContext.getConnEstablishedSpan()
- 例2:依赖关系是app-web分别依赖project-A,project-B,然后project-A依赖1.0版本的project-common(只有hello()方法),project-B依赖project-C,project-C依赖高版本2.0的project-common(高版本中还有goodbye()方法)
hello,world
Exception in thread "main" java.lang.NoSuchMethodError: com.xxx.service.HelloWorld.goodbye()Ljava/lang/String;
at com.xxx.adaptor.GoodbyeAdaptorService.goodbye(GoodbyeAdaptorService.java:11)
at com.xxx.service.GoodbyeService.goodbye(GoodbyeService.java:11)
at com.xxx.web.WebApp.goodbyeTest(WebApp.java:16)
at com.xxx.web.WebApp.main(WebApp.java:23)
- 为什么project-C依赖的project-common 2.0无法被加载,原因就是maven依赖采用的是就近依赖。从上图可以看出,project-common 1.0离app-web项目最近,因为project-common 2.0和app-web中间还隔着一个project-B。
- 我们查看app-web项目的依赖树结构,查看命令:mvn dependency:tree -Dverbose
- 由上图可以看到包依赖结构中,有两个不同版本的project-common,所以只需要排除掉一个导致报错的包即可(低版本没有方法的的包)
com.xxxproject-A1.0com.xxxproject-commoncom.xxxproject-B1.0
- 结果
二:依赖冲突发生的原因
- 大多数的依赖冲突发生的原因是因为maven的传递依赖会引入很多隐式的依赖,这些依赖可能会和我们显示依赖版本不一致。
- 依赖冲突的原因总结一句话就是,依赖的版本和实际使用的版本不一致导致的。
三:依赖原则优先级
- 原则一:最短路径优先
-
- 最短路径优先: 当依赖声明不在同一个POM文件中时,或者说存在依赖传递时,路径最短的jar包将被选为最终依赖。例如下面两个依赖路径:此时Jar2.0将被选为最终依赖。
- 原则二:第一声明者优先
-
- 第一声明者优先: 当依赖声明不在同一个POM文件中时,或者说存在依赖传递时,并且依赖传递长度相同时,最先声明的依赖将被选为最终依赖。此时Jar1.0将被作为最终依赖。
- 原则三:覆盖优先
-
- 覆盖优先: 当依赖声明在同一个POM文件中时,后面声明的依赖将覆盖前面声明的依赖。此时,Jar2.0将被选为最终依赖。
四:依赖冲突排查
- 根据报错原因能初步知道是那个jar包冲突引起的NoSuchMethodError,ClassNotFoundException,但是如何排查引发问题的jar包存在哪些版本的冲突呢?或者说该怎么去查找以及解决呢?
- 有两种办法来排查jar版本冲突,下面分别来介绍
五:Maven命令排查
- 首先列出常用的maven依赖冲突排查命令
mvn dependency:tree 查看maven依赖树(并不能查看到所有的传递依赖)
mvn dependency:tree -Dverbose 查看全部传递依赖依赖 ,-Dverbose参数,这时就必定是最全的了。
全是全了,但显示出来的东西太多,头晕目眩,有没有好法呢?当然有了,加上Dincludes或者Dexcludes说出你喜欢或讨厌,dependency:tree就会帮你过滤出来
如:mvn dependency:tree -Dverbose -Dincludes=com.alipay.common:tracer
- 1、如在IDEA中可以在Terminal中输入上述命令来查看maven依赖,这种方式不会保留历史记录,下次还是要重新输入
- 2、也可以用这种方式来输入命令排查,会保留历史记录,下次排查直接使用历史命令即可
- 执行结果:如果在执行结果中出现 omitted for conflict with 这样的字样,就表示项目中存在jar依赖冲突,最后编译使用的jar包是omitted for conflict with字样后面的版本
六:Maven helper命令排查
- 1、首先IDEA安装插件 maven helper插件(因为我已经安装了,所以没 install 的按钮了)
- 2、安装完成后,在 pom 文件下方会出现这个东西
- 3、点击AnaLyzer进去是这样的
- 4、接下来到重点了,选中冲突选项 conflicts,这一列都是存在冲突的包
- 5、以fastjson 为例,当然我们调错误的时候也可以直接搜索有问题的包,如下
- 6、选中右键就可以 Exclude 啦,想用1.2.28就把其他版本的Exclude掉,想用1.2.3就把其他版本的Exclude掉,是不是很方便。
七:解决方案
- 把不需要的jar包直接
<dependency>
<groupId>com.alipay.xxxxx</groupId>
<artifactId>xxxxx-xxxx</artifactId>
<version>1.0.0.20210225</version>
<exclusions>
<exclusion>
<groupId>com.alipay.xxxxx</groupId>
<artifactId>tracer</artifactId>
</exclusion>
</exclusions>
</dependency>