详解Maven JAR包冲突问题排查及解决方案

 更新时间:2021年03月03日 08:39:42   作者:RobertoHuang  
这篇文章主要介绍了Maven JAR包冲突问题排查及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

写这篇文章的初衷是因为今天在使用mvn dependency:tree命令时,突然想起一年前面试阿里的一道面试题。面试题是说假设线上发生JAR包冲突,应该怎么排查?我那时候的回答是IDEA有个Maven Helper的插件,可以帮忙分析依赖冲突,然后还有一种办法是如果一个类import的时候提示两个地方可导入,那就说明有冲突。现在回头想想确实太不专业了,以下是一次JAR包冲突的一个比较正规的流程,是通过整理几篇博客后总结的希望对大家也有帮助,如果有错误的地方也欢迎指出

GitHub地址:https://github.com/RobertoHuang

JAR冲突产生的原因

   Pom.xml
   /  \
  B    C
 / \   / \
 X  Y  X  M

在以上依赖关系中项目除了会引入B、C还会引入X、Y、M的依赖包,但是如果B依赖的X版本会1.0而C依赖的X版本为2.0时,那最后项目使用的到底是X的1.0版本还是2.0版本就无法确定了。这是就要看ClassLoader的加载顺序,假设ClassLoader先加载1.0版本那就不会加载2.0版本,反之同理

使用mvn -Dverbose dependency:tree排查冲突

[INFO] +- org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile
[INFO] +- org.apache.tomcat:tomcat-jsp-api:jar:7.0.70:compile
[INFO] | +- org.apache.tomcat:tomcat-el-api:jar:7.0.70:compile
[INFO] | \- (org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile - omitted for duplicate)
[INFO] +- net.sf.jasperreports:jasperreports:jar:5.6.0:compile
[INFO] | +- (commons-beanutils:commons-beanutils:jar:1.8.0:compile - omitted for conflict with 1.8.3)
[INFO] | +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] | +- commons-digester:commons-digester:jar:2.1:compile
[INFO] | | +- (commons-beanutils:commons-beanutils:jar:1.8.3:compile - omitted for duplicate)
[INFO] | | \- (commons-logging:commons-logging:jar:1.1.1:compile - omitted for duplicate)

递归依赖的关系列的算是比较清楚了,每行都是一个jar包,根据缩进可以看到依赖的关系

  • 最后写着compile的就是编译成功的
  • 最后写着omitted for duplicate的就是有JAR包被重复依赖了,但是JAR包的版本是一样的
  • 最后写着omitted for conflict with xx的,说明和别的JAR包版本冲突了,该行的JAR包不会被引入

该命令可配合-Dincludes和-Dexcludes进行使用,只输出自己感兴趣/不感兴趣的JAR
参数格式为:[groupId]:[artifactId]:[type]:[version]
每个部分(冒号分割的部分)是支持*通配符的,如果要指定多个格式则可以用,分割,如:

mvn dependency:tree -Dincludes=javax.servlet,org.apache.*

解决冲突,使用exclusion标签将冲突的JAR排除

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>dubbo</artifactId>
  <version>2.8.3.2</version>
  <exclusions>
    <exclusion>
      <artifactId>guava</artifactId>
      <groupId>com.google.guava</groupId>
    </exclusion>
    <exclusion>
      <artifactId>spring</artifactId>
      <groupId>org.springframework</groupId>
    </exclusion>  
  </exclusions>
</dependency>

解决了冲突后的树(解决冲突的策略是:就近原则,即离根近的依赖被采纳)

查看运行期类来源的JAR包

有时你以为解决了但是偏偏还是报类包冲突,典型症状是java.lang.ClassNotFoundException或Method不兼容等异常,这时你可以设置一个断点,在断点处通过下面这个工具类来查看Class所来源的JAR包

public class ClassLocationUtils {
  public static String where(final Class clazz) {
    if (clazz == null) {
      throw new IllegalArgumentException("null input: cls");
    }
    URL result = null;
    final String clazzAsResource = clazz.getName().replace('.', '/').concat(".class");
    final ProtectionDomain protectionDomain = clazz.getProtectionDomain();
    if (protectionDomain != null) {
      final CodeSource codeSource = protectionDomain.getCodeSource();
      if (codeSource != null) result = codeSource.getLocation();
      if (result != null) {
        if ("file".equals(result.getProtocol())) {
          try {
            if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) {
              result = new URL("jar:".concat(result.toExternalForm()).concat("!/").concat(clazzAsResource));
            } else if (new File(result.getFile()).isDirectory()) {
              result = new URL(result, clazzAsResource);
            }
          } catch (MalformedURLException ignore) {

          }
        }
      }
    }
    if (result == null) {
      final ClassLoader clsLoader = clazz.getClassLoader();
      result = clsLoader != null ? clsLoader.getResource(clazzAsResource) : ClassLoader.getSystemResource(clazzAsResource);
    }
    return result.toString();
  }
}

然后随便写一个测试设置好断点,在执行到断点出使用ALT+F8动态执行代码,如

ClassLocationUtils.where(Logger.class)

即可马上找到对应的JAR,如果这个JAR不是你期望的就说明是IDE缓存造成的,清除缓存后重试即可

到此这篇关于详解Maven JAR包冲突问题排查及解决方案的文章就介绍到这了,更多相关Maven JAR包冲突 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中Socket用法详解

    Java中Socket用法详解

    本文详细讲解了Java中Socket的用法,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • 详解如何用Java去除HTML标签

    详解如何用Java去除HTML标签

    在平时工作中,偶尔会用 Java 做一些解析HTML的工作。有时需要删除所有的HTML标签,只保留纯文字内容。这个问题在做过一些爬虫工作的朋友来说很简单。下面来说说,我们平时使用到的集中解析的方法
    2022-12-12
  • 如何使用Code128字体将文本转换为code128条形码

    如何使用Code128字体将文本转换为code128条形码

    这篇文章主要介绍了如何使用Code128字体将文本转换为code128条形码 ,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • Spring Cloud Zipkin服务端追踪服务

    Spring Cloud Zipkin服务端追踪服务

    这篇文章主要介绍了Spring Cloud Zipkin服务端追踪服务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java去掉小数点后面无效0的方案与建议

    Java去掉小数点后面无效0的方案与建议

    当前小数点后面的位数过多的时候,多余的0没有实际意义,下面这篇文章主要给大家介绍了关于Java去掉小数点后面无效0的方案与建议,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Spring Data MongoDB 数据库批量操作的方法

    Spring Data MongoDB 数据库批量操作的方法

    在项目开发中经常会批量插入数据和更新数据的操作,这篇文章主要介绍了Spring Data MongoDB 数据库批量操作的方法,非常具有实用价值,需要的朋友可以参考下
    2018-11-11
  • 读取xml文件中的配置参数实例

    读取xml文件中的配置参数实例

    下面小编就为大家带来一篇读取xml文件中的配置参数实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • java 文件名截取方法

    java 文件名截取方法

    在实际开发应用中会应到截取文件名,本文将介绍java中文件名的截取,需要了解的朋友可以参考下
    2012-11-11
  • java版数独游戏界面实现(二)

    java版数独游戏界面实现(二)

    这篇文章主要为大家详细介绍了java版数独游戏界面实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Java中WeakHashMap的工作原理详解

    Java中WeakHashMap的工作原理详解

    这篇文章主要介绍了Java中WeakHashMap的工作原理详解,WeakHashMap
    从名字可以得知主要和Map有关,不过还有一个Weak,我们就更能自然而然的想到这里面还牵扯到一种弱引用结构,因此想要彻底搞懂,我们还需要知道四种引用,需要的朋友可以参考下
    2023-09-09

最新评论