通过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>
而另一个模块需要使用 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>
我们从 maven repository 可以发现,阿里 easyexcel 1.0.2 依赖的 poi 也是 3.15,所以照理说应该是没问题的。
但是在接口调试的时候还是出问题了,而且异常信息很奇怪,不是看一眼就能知道问题原因的并解决的。
caused by: java.lang.abstractmethoderror: org.apache.xerces.dom.documentimpl.getxmlstandalone()z at com.sun.org.apache.xalan.internal.xsltc.trax.dom2to.setdocumentinfo(dom2to.java:377) at com.sun.org.apache.xalan.internal.xsltc.trax.dom2to.parse(dom2to.java:131) at com.sun.org.apache.xalan.internal.xsltc.trax.dom2to.parse(dom2to.java:98) at com.sun.org.apache.xalan.internal.xsltc.trax.transformerimpl.transformidentity(transformerimpl.java:693) at com.sun.org.apache.xalan.internal.xsltc.trax.transformerimpl.transform(transformerimpl.java:737) at com.sun.org.apache.xalan.internal.xsltc.trax.transformerimpl.transform(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 com.alibaba.excel.write.excelbuilderimpl.finish(excelbuilderimpl.java:64) at com.alibaba.excel.excelwriter.finish(excelwriter.java:95) at com.pingan.haofang.creams.common.utils.excelutil.writeexcel(excelutil.java:71) ...... ... 65 common frames omitted
提取关键信息,可以看到错误类型 java.lang.abstractmethoderror
,这个错误类型望名知义:抽象方法错误。这种类型的错误和我们上面说的 classnotfoundexception
类似,很大可能就是 jar包依赖冲突所导致的。
异常定位
那我们来定位下是哪个 jar 包冲突了,只需要将冲突的 jar 包排除掉,留下正确的就可以了。
我们可以看到错误类型是 java.lang.abstractmethoderror
,错误类型后面是具体的错误信息描述 :
org.apache.xerces.dom.documentimpl.getxmlstandalone()z
,意思是在包 org.apache.xerces.dom
下的类 documentimpl 它的方法 getxmlstandalone() 调用出现了错误。
那么具体是谁在调用呢?我们在异常信息的紧密下一行可以看到如下这一行代码:
at com.sun.org.apache.xalan.internal.xsltc.trax.dom2to.setdocumentinfo(dom2to.java:377)
在包路径 com.sun.org.apache.xalan.internal.xsltc.trax 下, dom2to 类代码的的第377行,有个 setdocumentinfo 方法,我们鼠标左键点进去,在该行加个 debug 断点。
我们发现这个 dom2to 类是 jdk1.8中 rt.jar 包里面的,具体类路径如下:
通过断点调试得知,这个 document 对象是 documentimpl 实例,
这个documentimpl 的真实路径也是 jdk1.8中 rt.jar 包里面的,它是 coredocumentimpl 的子类,coredocumentimpl 是接口document 的实现类。
package com.sun.org.apache.xerces.internal.dom; public class documentimpl extends coredocumentimpl implements documenttraversal, documentevent, documentrange { ...... } coredocumentimpl package com.sun.org.apache.xerces.internal.dom; public class coredocumentimpl extends parentnode implements document { ...... }
我们在 coredocumentimpl 类中第983行发现了 getxmlstandalone 方法。
这时报错原因赤条条的摆在我们面前了,显而易见,dom2to类中 setdocumentinfo 方法的参数 document 是属于 jdk1.8 中 rt.jar 包下类路径 com.sun.org.apache.xerces.internal.dom 下的实现类 documentimpl。而我们报错的信息提示中是:
caused by: java.lang.abstractmethoderror: org.apache.xerces.dom.documentimpl.getxmlstandalone()z
这个 org.apache.xerces.dom.documentimpl 明显不属于我们 jdk1.8 的 rt.jar 包,而且也没有 getxmlstandalone 这个方法。
所以得知,我的项目中 jar 包依赖冲突了,我们只需要排除掉 org.apache.xerces.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 ,具体的插件下载可以参考前面的内容,下载好插件后,我们打开 pom.xml 文件,在pom.xml 文件的左下方有个 dependency analyzer ,我们点击之后显示如下:
- conflicts :展示所有冲突。
- all dependencies as list :以列表的方式展示所有依赖。
- all dependencies as tree :以树形的方式展示所有依赖。
我们输入 xerces,选择以树形展示所有依赖,得到如下的信息显示。
清晰明了,原来这个罪魁祸首是被 file-web-sdk 带进来的,我们右键选择 jump to source 或者 f4 定位到这个 jar 在 pom.xml 的依赖引入位置,如下图所示,我们通过 exclusion 标签排除 xercesimpl 的引入即可。
<dependency> <groupid>com.xx.xx.gov.fileservice</groupid> <artifactid>file-web-sdk</artifactid> <exclusions> <exclusion> <groupid>xerces</groupid> <artifactid>xercesimpl</artifactid> </exclusion> </exclusions> </dependency>
再次启动项目,测试接口发现功能正常了,整个排查过程也就结束了,idea的功能还是很强大的。
总结
很多时候的 jar 包冲突,有些是我们很容易排除,例如在pom.xml 中我们就可以发现一些重复引入,但是版本不相同的依赖。还有一些是其他依赖传递依赖进来的,我们在 pom.xml 文件中不能很直观的发现,这时候我们借助工具可以发现这种冲突的依赖。
但是还有一些是更隐秘的冲突,就像本文中描述的依赖冲突,这时候我们需要分析异常信息,并定位冲突的原因和找到具体冲突的依赖引入,最后将它排除就可以了。
本文比较详细的介绍了异常的分析和冲突的定位,以及最后的排除。类似的依赖冲突基本都可以参考上述的方式进行排查,希望通过本篇文章对大家解决项目中依赖冲突有所帮助。
以上所述是小编给大家介绍的通过idea快速定位和排除依赖冲突问题 ,希望对大家有所帮助