通过IDEA快速定位和排除依赖冲突
前⾔
我们程序员在开发的时候经常会遇到各种各样的 BUG 问题,其中⼤部分是业务逻辑异常,还有⼀些是代码书写不规范造成的异常例如:NullPointException(NPE),IndexOutOfBoundsException 等等,其实这些我们都好定位和修复。但是还有⼀些运⾏时异常定位起来是特别头疼的,那就是jar 包冲突引起的异常。
⼀般程序在运⾏时发⽣类似于java.lang.ClassNotFoundException,Method not found: '......',或者莫名其妙的异常信息,这种情况⼀般很⼤可能就是 jar包依赖冲突的问题引起的了。
⾄于为什么会发⽣ jar包依赖冲突?这种问题⼤致可以归纳为如下⼏个原因:
版本不匹配,⾼版本依赖了低版本,或者低版本依赖了⾼版本。例如引⼊第三⽅库,但是第三⽅库基于的是 JDK7,⽽你们项⽬使⽤的是JDK8。
重复引⼊不同版本jar包,造成使⽤错误。很多时候我们引⼊第三⽅轮⼦,它们依赖引⼊某个基础⼯具使⽤的是 v 1.0 的 jar,但是我们项⽬中⾃⼰也引⼊了该 jar ,但是版本是 v 2.3,这时就会造成项⽬中使⽤同⼀个组件但是依赖了两个不同版本的jar,冲突就会发⽣。
可以看到,其实总的来说 jar 包冲突的主要原因就是依赖的版本冲突。
异常发⽣
项⽬中需要导出报表,技术选型的时候,⼀般是选⽤ Apache POI,但是 POI 的使⽤⽅式⽐较基础,开发量⼤,容易出现内存溢出的问题。
考虑到阿⾥开源了⼀套解析和⽣成Excel的⼯具 - EasyExcel,具有避免内存溢出OOM的情况发⽣,⽽且使⽤⽅便简单,所以就将它引⼊到了我们的项⽬中,具体的使⽤版本是 1.0.2。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.0.2</version>
</dependency>
下载apache⽽另⼀个模块需要使⽤ POI 的将 Word 转成 PDF 的功能,所以同时⼜引⼊了如下 POI 的依赖:
<!-- poi utils -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
我们从可以发现,阿⾥ EasyExcel 1.0.2 依赖的 POI 也是 3.15,所以照理说应该是没问题的。
但是在接⼝调试的时候还是出问题了,⽽且异常信息很奇怪,不是看⼀眼就能知道问题原因的并解决的。
Caused by: java.lang.AbstractMethodError: s.XmlStandalone()Z
at apache.xalan.ax.DOM2TO.setDocumentInfo(DOM2TO.java:377)
at apache.xalan.ax.DOM2TO.parse(DOM2TO.java:131)
at apache.xalan.ax.DOM2TO.parse(DOM2TO.java:98)
at apache.xalan.ansformIdentity(TransformerImpl.java:693)
at apache.xalan.ansform(TransformerImpl.java:737)
at apache.xalan.ansform(TransformerImpl.java:351)
at org.apache.poi.openxml4j.opc.StreamHelper.saveXmlInStream(StreamHelper.java:80)
at org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller.marshallRelationshipPart(ZipPartMarshaller.java:181)
at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:560)
at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1557)
at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:248)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.write(SXSSFWorkbook.java:941)
at l.write.ExcelBuilderImpl.finish(ExcelBuilderImpl.java:64)
at l.ExcelWriter.finish(ExcelWriter.java:95)
at com.amsmon.utils.ExcelUtil.writeExcel(ExcelUtil.java:71)
......
... 65 common frames omitted
提取关键信息,可以看到错误类型 java.lang.AbstractMethodError,这个错误类型望名知义:抽象⽅法错误。这种类型的错误和我们上⾯说的ClassNotFoundException 类似,很⼤可能就是 Jar包依赖冲突所导致的。
异常定位
那我们来定位下是哪个 jar 包冲突了,只需要将冲突的 jar 包排除掉,留下正确的就可以了。
我们可以看到错误类型是java.lang.AbstractMethodError,错误类型后⾯是具体的错误信息描述
:s.XmlStandalone()Z,意思是在包s.dom下的类DocumentImpl它的⽅法getXmlStandalone()调⽤出现了错误。
那么具体是谁在调⽤呢?我们在异常信息的紧密下⼀⾏可以看到如下这⼀⾏代码:
at apache.xalan.ax.DOM2TO.setDocumentInfo(DOM2TO.java:377)
在包路径apache.xalan.ax下,DOM2TO 类代码的的第377⾏,有个setDocumentInfo⽅法,我们⿏标左键点进去,在该⾏加个Debug 断点。
我们发现这个 DOM2TO 类是 JDK1.8中 rt.jar 包⾥⾯的,具体类路径如下:
通过断点调试得知,这个 document 对象是 DocumentImpl 实例,
这个DocumentImpl 的真实路径也是 JDK1.8中 rt.jar 包⾥⾯的,它是 CoreDocumentImpl 的⼦类,CoreDocumentImpl 是接⼝Document 的实现类。package s.internal.dom;
public class DocumentImpl
extends CoreDocumentImpl
implements DocumentTraversal, DocumentEvent, DocumentRange {
......
}
CoreDocumentImpl
package s.internal.dom;
public class CoreDocumentImpl
extends ParentNode implements Document {
......
}
我们在 CoreDocumentImpl 类中第983⾏发现了getXmlStandalone⽅法。
这时报错原因⾚条条的摆在我们⾯前了,显⽽易见,DOM2TO类中 setDocumentInfo ⽅法的参数 Document 是属于 JDK1.8 中 rt.jar 包下类路径
s.internal.dom下的实现类 DocumentImpl。⽽我们报错的信息提⽰中是:
Caused by: java.lang.AbstractMethodError: s.XmlStandalone()Z
这个s.dom.DocumentImpl明显不属于我们 JDK1.8 的 rt.jar 包,⽽且也没有 getXmlS
tandalone 这个⽅法。
所以得知,我的项⽬中 jar 包依赖冲突了,我们只需要排除掉s.dom.DocumentImpl所属的 jar 包就可以了。如何排除呢?
排除冲突
我们在 IDEA 中双击 Shift 键,输⼊ DocumentImpl,得到如下结果:
可以发现,这⾥有两个 CoreDocumentImpl,⼀个是我们的 JDK1.8的,⼀个是属于 xerce的,⽽且确实在依赖的 maven jar 包中发现了 xercesImpl-2.4.0.jar,这个 jar包就是需要排除的 jar包。
发现了冲突的 jar包,我全局搜索关键字 xerces,并没有发现哪⼀个 pom 中有依赖的代码,所以很可能是其他的 jar 包传递依赖进来的。
我们借助 IDEA 的 maven ⼯具,在 maven 栏右键项⽬模块,选择 show Dependencies 或 Ctrl + Shift + Alt + U,这时候会展⽰当前模块的 jar 包依赖图,如下:
虽然这⾥展⽰了很多冲突的jar包,其中红线连接的就是冲突的jar 包,但是我们 Ctrl + F 查询 xerces 还是没有结果。
所以我们需要额外的⽅式来解决,这时我想到了 IDEA 有个插件 Maven Helper,具体的插件下载可以参考,下载好插件后,我们打开 l ⽂件,在l ⽂件的左下⽅有个 Dependency Analyzer,我们点击之后显⽰如下:
Conflicts:展⽰所有冲突。
All Dependencies as List:以列表的⽅式展⽰所有依赖。
All Dependencies as Tree:以树形的⽅式展⽰所有依赖。
我们输⼊ xerces,选择以树形展⽰所有依赖,得到如下的信息显⽰。
清晰明了,原来这个罪魁祸⾸是被 file-web-sdk 带进来的,我们右键选择 Jump To Source或者 F4 定位到这个 jar 在 l 的依赖引⼊位置,如下图所⽰,我们通过 exclusion 标签排除 xercesImpl 的引⼊即可。
<dependency>
<groupId&gov.fileservice</groupId>
<artifactId>file-web-sdk</artifactId>
<exclusions>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
</exclusions>
</dependency>
再次启动项⽬,测试接⼝发现功能正常了,整个排查过程也就结束了,IDEA的功能还是很强⼤的。
总结
很多时候的 jar 包冲突,有些是我们很容易排除,例如在l 中我们就可以发现⼀些重复引⼊,但是版本不相同的依赖。还有⼀些是其他依赖传递依赖进来的,我们在 l ⽂件中不能很直观的发现,这时候我们借助⼯具可以发现这种冲突的依赖。
但是还有⼀些是更隐秘的冲突,就像本⽂中描述的依赖冲突,这时候我们需要分析异常信息,并定位冲突的原因和到具体冲突的依赖引⼊,最后将它排除就可以了。
本⽂⽐较详细的介绍了异常的分析和冲突的定位,以及最后的排除。类似的依赖冲突基本都可以参考上述的⽅式进⾏排查,希望通过本篇⽂章对⼤家解决项⽬中依赖冲突有所帮助。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论