通过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快速定位和排除依赖冲突问题 ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • idea启动项目报端口号冲突或被占用的解决方法

    错误异常如下: java.rmi.server.ExportException: Port already in use: 1099; nested exception is: java.net.BindException: Address already in use: JVM_Bind 解决方法: 方法一: 1.win键 + R,输入cmd然后回车,进入DOS命令窗口 2.根据端口号查程序的进程号  netstat -ano | findstr 占用端口号(1099) 3. 根据程序的进程号

  • idea中解决maven包冲突的问题(maven helper)

    日常开发中经常会遇到xxx.class 找不到的异常,但是这个类确实存在我们的项目中,就会感觉很离奇,其实这就是包冲突的问题 冲突问题 比如项目中引用了两个 fastjson.jar的版本,分别为 fastjson:1.2.28 fastjson:1.2.3 我们用到了1.2.28中的某个类, 比如 A类,在版本更新中 1.2.3版本去掉了这个类,然而我们项目中maven 却把1.2.3的 jar 打包进去了,那我们就会报异常,不存在这个 class,但是我们调错误的时候却发现这个类存在,那我们

  • IntelliJ IDEA同步代码时版本冲突而产生出的incoming partial文件问题的解决办法

    在用IntelliJ IDEA 中同步代码的时候,发现在版本控制的 incoming  下面出现了个 partial 的文件夹的东西,里面的文件就是因为版本冲突而产生出的问题. 问题产生的原因: 是我把partial目录下的文件在我自己的项目里面给修改了目录(新建了一个文件夹然后将那个文件移动到新的文件夹下面.当然我还没提交这个文件的时候,我所在的team里面有人又改动了这个文件,并且还提交了这个文件),所以我发现有代码更新的时候,我更新到我的本地项目里面的时候,就会在svn的 9 versio

  • 通过IDEA快速定位和排除依赖冲突问题

    前言 我们程序员在开发的时候经常会遇到各种各样的 BUG 问题,其中大部分是业务逻辑异常,还有一些是代码书写不规范造成的异常例如:NullPointException(NPE),IndexOutOfBoundsException 等等,其实这些我们都好定位和修复.但是还有一些运行时异常定位起来是特别头疼的,那就是 jar 包冲突引起的异常. 一般程序在运行时发生类似于 java.lang.ClassNotFoundException , Method not found: '......' ,或

  • 导入takephoto库编译失败与glide库冲突应排除依赖

    导入takephoto库编译失败与glide库冲突 当您的项目中导入了glide库, 同时也导入了takephoto库时, 出现编译失败. 编译报错指向于Glide库某文件 原因: 这是因为takephoto库中本身依赖了三个库,其中一个库是照片墙的库multipleimageselect 从github上打开该开的build.gradle可以看到该库又依赖了glide库. 所以发生依赖冲突问题. 以现在各库更新情况, takephoto是4.0.3 multipleimageselect 是1

  • SpringBoot快速入门及起步依赖解析(实例详解)

    目录 一.SpringBoot简介 1.1 SpringBoot快速入门 1.1.1 开发步骤 1.1.2 对比 1.1.3 官网构建工程 1.1.4 SpringBoot工程快速启动 1.2 SpringBoot概述 1.2.1 起步依赖 1.2.2 程序启动 1.2.3 切换web服务器 一.SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 使用了 Spring 框架后已经简化了我们的开

  • .NET程序调试技巧(一):快速定位异常的一些方法

    作为一个程序员,解BUG是我们工作中常做的工作,甚至可以说解决问题能力是一个人工作能力的重要体现.因为这体现了一个程序员的技术水平.技术深度.经验等等. 那么在我们解决BUG的过程中,定位问题是非常重要的.有句话叫"发现问题是解决问题的一半. 本文讲述就快速定位异常(专指.NET程序异常)的方法.包括在本机定位异常,在客户环境定位.net程序异常,在客户环境定位SilverLight异常. 一:定位本机异常 在我们本机定位异常很容易.假设我们都是使用的的VisualStudio,那么只需要在调试

  • Android Studio Gradle依赖冲突解决方法

    前言 本文主要给大家介绍了Android Studio Gradle依赖冲突解决的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1. 查看依赖树 ./gradlew dependencies 2. 解决依赖冲突 一旦在构建中存在依赖冲突,开发人员需要决定哪个版本的库最终包含在构建中,有许多解决冲突的方法. 1. 逐个排除 compile('junit:junit:4.12'){ exclude group : 'org.hamcrest',module:'hamcre

  • 详解maven依赖冲突以及解决方法

    什么是依赖冲突 依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成类包版本冲突 依赖冲突的原因 依赖冲突很经常是类包之间的间接依赖引起的.每个显式声明的类包都会依赖于一些其它的隐式类包,这些隐式的类包会被maven间接引入进来,从而造成类包冲突 如何解决依赖冲突 首先查看产生依赖冲突的类jar,其次找出我们不想要的依赖类jar,手工将其排除在外就可以了.具体执行步骤如下 1.查看依赖冲突 a.通过dependency:tree是命令来检查版本冲突 mvn -Dverbose dep

  • 解决Mybatis-plus和pagehelper依赖冲突的方法示例

    简介 MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发.提高效率而生. 启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 Mybati-plus本身自带分页功能,但是我个人一直是使用pagehelper进行分页,所以在pom中添加了pagehelper依赖,但是运行项目后发现jar包冲突,面对冲突我们应该怎么解决它呢,看完如下内容便可轻松解决 先看依赖 <!-- mbatis-plus --> &

  • Mybatis-plus与Mybatis依赖冲突问题解决方法

    错误描述 An attempt was made to call a method that does not exist. The attempt was made from the following location: com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder.getLanguageDriver(MybatisMapperAnnotationBuilder.java:369) The following met

  • 一分钟快速定位Android启动耗时问题

    目录 前言 1. 接入Tencent Matrix 2. 改造Application子类 3.运行,快速定位 总结 前言 Tencent Matrix默认无法监测Application冷启动的耗时方法,本文介绍了如何改造Matrix支持冷启动耗时方法监测.让你一分钟就能给App启动卡顿号脉. 1. 接入Tencent Matrix 1.1 在你项目根目录下的 gradle.properties 中配置要依赖的 Matrix 版本号,如: MATRIX_VERSION=1.0.0 1.2 在你项目

  • 关于Maven依赖冲突解决之exclusions

    目录 Maven依赖冲突解决之exclusions 1. 背景 2. 解决方式 场景 解决方式 Maven解决依赖冲突总结 实例分析 解决办法 命令分析 小试牛刀 Maven依赖冲突解决之exclusions 1. 背景 作为java生态下开发者,往往需要使用大量线程的第三方库,一般都是以jar包形式存在. maven作为事实上主流的jar包依赖管理工具,Idea和Eclipse都支持创建maven工程来管理jar包依赖. 使用maven进行jar包依赖管理时,maven会自行管理jar包及其依

随机推荐