经过IDEA快速定位和排除依赖冲突

前言

咱们程序员在开发的时候常常会遇到各类各样的 BUG 问题,其中大部分是业务逻辑异常,还有一些是代码书写不规范形成的异常例如:NullPointException(NPE),IndexOutOfBoundsException 等等,其实这些咱们都好定位和修复。可是还有一些运行时异常定位起来是特别头疼的,那就是 jar 包冲突引发的异常。html

通常程序在运行时发生相似于 java.lang.ClassNotFoundExceptionMethod not found: '......',或者莫名其妙的异常信息,这种状况通常很大可能就是 jar包依赖冲突的问题引发的了。java

至于为何会发生 jar包依赖冲突?这种问题大体能够概括为以下几个缘由:程序员

  • 版本不匹配,高版本依赖了低版本,或者低版本依赖了高版本。例如引入第三方库,可是第三方库基于的是 JDK7,而大家项目使用的是JDK8。
  • 重复引入不一样版本jar包,形成使用错误。不少时候咱们引入第三方轮子,它们依赖引入某个基础工具使用的是 v 1.0 的 jar,可是咱们项目中本身也引入了该 jar,可是版本是 v 2.3,这时就会形成项目中使用同一个组件可是依赖了两个不一样版本的jar,冲突就会发生。

能够看到,其实总的来讲 jar 包冲突的主要缘由就是依赖的版本冲突。web

异常发生

项目中须要导出报表,技术选型的时候,通常是选用 Apache POI,可是 POI 的使用方式比较基础,开发量大,容易出现内存溢出的问题。apache

考虑到阿里开源了一套解析和生成Excel的工具 - EasyExcel,具备避免内存溢出OOM的状况发生,并且使用方便简单,因此就将它引入到了咱们的项目中,具体的使用版本是 1.0.2。dom

<dependency>
   <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.0.2</version>
</dependency>

而另外一个模块须要使用 POI 的将 Word 转成 PDF 的功能,因此同时又引入了以下 POI 的依赖:xss

<!-- 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,因此照理说应该是没问题的。maven

可是在接口调试的时候仍是出问题了,并且异常信息很奇怪,不是看一眼就能知道问题缘由的并解决的。工具

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 DependenciesCtrl + 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 文件中不能很直观的发现,这时候咱们借助工具能够发现这种冲突的依赖。

可是还有一些是更隐秘的冲突,就像本文中描述的依赖冲突,这时候咱们须要分析异常信息,并定位冲突的缘由和找到具体冲突的依赖引入,最后将它排除就能够了。

本文比较详细的介绍了异常的分析和冲突的定位,以及最后的排除。相似的依赖冲突基本均可以参考上述的方式进行排查,但愿经过本篇文章对你们解决项目中依赖冲突有所帮助。

相关文章
相关标签/搜索