详解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包冲突 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Maven jar包冲突的解决方案

    现象 创建一个maven工程,引入spring-context包. <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.8.RELEASE</version> </dependency> 此时看左侧的lib,我们发现引入了一个坐标,多出了很多的jar包,这

  • 使用maven工具解决jar包冲突或重复加载的问题

    在使用maven开发项目的过程中,经常会遇到jar包重复加载或者jar包冲突的问题的,但是由于有些jar是由于maven的依赖加载自动加载进来的, 而不是开发者自己配置的,特别是当项目中pom中配置的jar包依赖本身很多时,开发者靠自己的经验,有时很难找出是哪个jar的加载导致加载了 多余的依赖jar,从而产生冲突. 今天刚好遇到一个借用eclipse中的maven插件解决jar包依赖冲突的问题,分享一下. 项目中出现的问题如下: Caused by: java.lang.NoClassDefF

  • Maven中jar包冲突原理与解决办法

    Maven中jar包冲突是开发过程中比较常见而又令人头疼的问题,我们需要知道 jar包冲突的原理,才能更好的去解决jar包冲突的问题.本文将从jar包冲突的原理和解决两个方面阐述Maven中jar包冲突的解决办法. 一.Maven中jar包冲突产生原因 MAVEN项目运行中如果报如下错误: Caused by:java.lang.NoSuchMethodError Caused by: java.lang.ClassNotFoundException 十有八九是Maven jar包冲突造成的.那

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

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

  • 详解Maven项目Dependencies常见报错及解决方案

    个人最近项目中所遇到的问题,记录下便自己和同样遇到问题的小伙伴提供一个参考. 通常Maven里报红波浪线的常见问题 ①可能是子工程没有继承父工程 注意父工程中有中对应的module: <groupId>com.fred.parent</groupId> <artifactId>mall</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>

  • Jar包冲突问题原理及解决方案

    背景: 新需求需要引入新jar包,引入后发现本地启动没有报错,发到测试环境提示某个bean无法创建,nested exception is java.lang.VerifyError: Bad type on operand stack. 解决: 1,没有引入新jar包之前是没有提示这个报错了,猜测是jar包冲突. 2,到测试环境的lib目录根据新引入jar包的关键字找到如图所示的jar,图中最下面三个jar是新功能需要引入的jar,多了excelmagic-1.3-20190806.10055

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

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

  • 详解Maven POM(项目对象模型)

    POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等. 执行任务或目标时,Maven 会在当前目录中查找 POM.它读取 POM,获取所需的配置信息,然后执行目标. POM 中可以指定以下配置: 项目依赖 插件 执行目标 项目构建 profile 项目版本 项目开发者列表 相关邮件列表信息 在创建 POM 之前,我们首先需要描述项目组 (groupId), 项目的

  • Hbase、elasticsearch整合中jar包冲突的问题解决

    问题背景 再数据平台中,项目搭建需要使用es和HBASE搭建数据查询接口,整合的过程中出现jar包冲突的bug :com.google.common.base.Stopwatch.()V from class org.apache.hadoop.hbase.zookeeper.MetaTableLocator org.apache.hadoop.hbase.DoNotRetryIOException: java.lang.IllegalAccessError: tried to access m

  • 详解Maven私服Nexus的安装与使用

    本文介绍了详解Maven私服Nexus的安装与使用,分享给大家,具体如下: 1.安装 1.1 安装docker并加速 yum update && yum install docker sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://y7u9p3i0.mirror.aliyuncs.com"

随机推荐