jar依赖冲突解决实践

前言

随着功能的增多,各种中间件的引入。应用以来的各种jar的规模极具膨胀,出现jar冲突和Class冲突的问题层出不穷,让人不胜其扰。本文针对冲突,提供一个排查和定位问题的最佳实践。实践中尽量不借助第三方工具,而使用maven或者Linux的自带命令行。

Maven构建的应用的jar冲突

目前最为最流行的项目构建和管理工具,在目前的互联网应用中被广泛使用。maven框架很大的一个便利就是对于jar的依赖管理,它自然提供了一些工具帮助开发者进行依赖分析。maven存在坐标的概念。groupId,artifactId,version三个维度定位到一个唯一的jar。

1
2
3
4
5
6
7
<dependency>
<groupId>com.taobao.diamond</groupId>
<artifactId>diamond-client</artifactId>
<version>3.6.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

对于版本,有一个很宽泛的范围

[3.6.0,4.0.0) 要求的依赖版本>=3.6.0且<4.0.0

[,3.6.0] 要求的依赖版本<=3.6.0
对于应用来讲,还是固定一个版本为好,夸版本有太多不可预知的情况存在。

静态代码检查

通过mvn dependency:tree 命令查看依赖树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[INFO] \- com.alibaba.china.shared:credit_shared.ruleengine.biz:jar:1.0-SNAPSHOT:compile
[INFO] +- com.alibaba.china.shared:credit_shared.ruleengine.api:jar:1.0-SNAPSHOT:compile
[INFO] +- com.alibaba.china.shared:credit_shared.ruleengine.dal:jar:1.0-SNAPSHOT:compile
[INFO] +- org.antlr:antlr:jar:3.3:compile
[INFO] +- org.antlr:antlr-runtime:jar:3.3:compile
[INFO] | \- org.antlr:stringtemplate:jar:3.2.1:compile
[INFO] +- org.mvel:mvel2:jar:2.1.3.Final:compile
[INFO] +- org.drools:knowledge-api:jar:5.5.0.Final:compile
[INFO] +- org.drools:drools-core:jar:5.5.0.Final:compile
[INFO] | \- org.drools:knowledge-internal-api:jar:5.5.0.Final:compile
[INFO] \- org.drools:drools-compiler:jar:5.5.0.Final:compile
[INFO] +- org.antlr:antlr:jar:2.7.7:compile
[INFO] +- org.eclipse.jdt.core.compiler:ecj:jar:3.5.1:compile
[INFO] \- com.thoughtworks.xstream:xstream:jar:1.4.1:compile
[INFO] +- xmlpull:xmlpull:jar:1.1.3.1:compile
[INFO] \- xpp3:xpp3_min:jar:1.1.4c:compile

通过静态代码扫描的方式,能分析出来jar之间的依赖关系。举例credit_shared.ruleengine.biz-1.0-SNAPSHOT.jar依赖了antlr-3.3.jar。org.antlr:antlr-3.3.jar在maven中的坐标是

1
2
3
4
5
6
7
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>3.3</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

依赖仲裁

从上面的依赖树,出现了另外一个版本的jar——org.antlr:antlr:jar:2.7.7:compile,这就出现了依赖仲裁的问题。
maven 2.2.1版本仲裁规则:

  1. 按照项目总POM的DependencyManager版本声明进行仲裁(覆盖),但无警告
  2. 如无仲裁声明,则按照依赖最短路径确定版本
  3. 若相同路径,有严格区间限定的版本优先
  4. 若相同路径,无版本区间,则按照先入为主原则

如要解决冲突问题,很多时候都用到exclusions,如A->B->D(v1),A->C-D(v2),要指定A->D(v1),则需要在声明C的依赖时候通过exclusions列表排除掉对D(v2)的依赖。

要更好理解依赖仲裁,需要了解以下附带知识。

maven classpath

maven中有三种classpath:

  1. 编译classpath:编译项目代码,依赖的jar会被引入到classpath
  2. 测试classpath:编译和执行测试部分代码,如单元测试,集成测试,依赖会被引入到classpath
  3. 运行classpath:实际运行代码的时候,依赖的jar会被引入到classpath

scope:依赖范围

scope就是为了解决jar在classpath中的可见性。scope有以下几个可选项
compile:默认值,对编译classpath、测试classpath、运行classpath都有效,在三个阶段都需要指定的jar
provided:编译和测试可用,不会被传递依赖,不会被打包。例:依赖于web容器中的提供的一个jar包,在编译的时候需要加入依赖(web容器还没有介入),运行的时候由web容器来提供。如servlet-api。
test:执行单元测试时可用,不会被打包,不会被传递依赖
runtime:运行和测试时需要,但编译时不需要。最典型的例子是JDBC的驱动,编译时只需要提供驱动的API即可,在运行和测试阶段,需要加载到具体的驱动实现。
system:跟provided一致,显示制定依赖路径,一般是指定了本地的仓库之外的类库文件。可能造成不可依赖性,不推荐使用。

传递性依赖

A->B,B->C,则A->C。这是传递性依赖。依赖是有范围的,A->B,B->C的依赖范围决定了A->C的依赖范围。

A->C compile provided test runtime
compile compile     runtime  
provided provided provided   provided  
test test     test  
runtime runtime     runtime  
可选依赖
1
2
3
4
5
6
7
8
    <dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>3.3</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>

A->B,B->C(可选),B->D(可选),则A不会通过传递依赖到C或者D。

非Maven项目或者不同坐标的jar出现Class冲突,

上面介绍的是jar相同而版本不同,如antlr-3.3.jar,antlr-3.2.jar类似情况的冲突解决方案。这种情况一般出现在中间件升级。下面介绍坐标不同,如antlr-old-3.3.jar、antlr-new.jar,而jar中包含了类路径完全相同的类的情况。
出现这种情况,一般的异常提示都是“XXX类has no such method XXXX”之类的。这些异常提示基本可以定位成Class不是你要的Class,只不过Class的全路径是相同的,最大的可能也就是ClassLoader加载了另外一个jar的同名类。所以,首先要排查到该类是从哪个具体jar中来的。JVM提供了这样的功能,查看加载类的情况

1
java -verbose:class

类加载情况,如SubscriptionInfo类加载自file:/home/zhao/web-deploy/jettyserver/tmp/jetty-0.0.0.0-34200-root.war--any-/webinf/WEB-INF/lib/pc2.common-1.2.5.jar

1
2
[Loaded com.alibaba.pc2.common.remote.subscription.SubscriptionInfo from file:/home/zhao/web-deploy/jetty_server/tmp/jetty-0.0.0.0-34200-root.war-_-any-/webinf/WEB-INF/lib/pc2.common-1.2.5.jar]
[Loaded com.alibaba.pc2.common.domain.productpackage.PackageInfo from file:/home/zhao/web-deploy/jetty_server/tmp/jetty-0.0.0.0-34200-root.war-_-any-/webinf/WEB-INF/lib/pc2.common-1.2.5.jar]

而同样在pmap pid可以进程的内存镜像。 jps -v查看pid,再通过pmap pid > map.txt,从map.txt中查到

1
00007fa4ae174000     20K r-xs-  /home/zhao/web-deploy/jetty_server/tmp/jetty-0.0.0.0-34200-root.war-_-any-/webinf/WEB-INF/lib/pc2.common-1.2.5.jar

得到确认后,如果是maven工程,则通过依赖树查询到是具体哪个jar依赖了这个错误引用,在pom文件中exclusions掉该jar即可。
如果是非maven工程,则通过其他方式把错误引用排除掉即可。

mvn dependency:tree的更多相关文章

  1. 查看maven项目的依赖关系 mvn dependency:tree

    maven-dependency-plugin最大的用途是帮助分析项目依赖,dependency:list能够列出项目最终解析到的依赖列表,dependency:tree能进一步的描绘项目依赖树,de ...

  2. mvn dependency:tree的用法

    一.参考文档 https://maven.apache.org/plugins/maven-dependency-plugin/examples/resolving-conflicts-using-t ...

  3. 7) mvn dependency:tree

    http://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html mvn dependency:tree 查看 <de ...

  4. Maven类包冲突终极三大解决技巧 mvn dependency:tree

    Maven对于新手来说是<步步惊心>,因为它包罗万象,博大精深,因为当你初来乍到时,你就像一个进入森林的陌生访客一样迷茫. Maven对于老手来说是<真爱配方>,因为它无所不能 ...

  5. maven 查看依赖树结构命令mvn dependency:tree

    使用maven 管理项目的依赖,可以使用如下命令查看依赖树结构: mvn dependency:tree 如下图是使用idea的终端执行命令的局部图: 也可以使用如下命令将输出定向到某个文件,这样就可 ...

  6. maven dependency:tree中反斜杠的含义

    摘自:http://www.708luo.com/posts/2013/11/maven-dependency-slash-mark/ 一个mvn dependency:tree命令执行的输出如下: ...

  7. npm运行出错npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree

    npm运行出错npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree 场景复现: 使用vue CLI创建项 ...

  8. websphere安装和mvn dependency:copy-dependencies

    http://www.blogjava.net/paulwong/archive/2009/09/19/295657.html http://ljhzzyx.blog.163.com/blog/sta ...

  9. npm版本兼容导致的npm ERR! ERESOLVE unable to resolve dependency tree

    当团队项目中,团队成员的npm包管理工具版本不一致时执行npm install报错: npm -v查看版本信息:7.x与6.x之间的兼容问题 解决方案: 一:升级或降级npm版本,保持一致npm in ...

随机推荐

  1. 【代码笔记】iOS-UIActionSheet动态添加按钮

    一,效果图. 二,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIVi ...

  2. CentOS7上搭建LDAP-PDC并且将windows 2008 R2加入LDAP-PDC域

    由于测试原因,要涉及到将windows机器加入到ldap域,所以查看各种文档进行ldap-pdc域的搭建,并成功将windows 2008r2加入到ldap-pdc域中.下面简单记录一下搭建过程 Li ...

  3. JS原生添加删除class的方法

    之前习惯了使用jquery的addClass的方法,然后就去找了下别人写的代码. [javascript] view plain copy function hasClass(obj,cls) { r ...

  4. 墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System

    GIS理论(墨卡托投影.地理坐标系.地面分辨率.地图比例尺.Bing Maps Tile System) 墨卡托投影(Mercator Projection),又名"等角正轴圆柱投影&quo ...

  5. Android手机上,利用bat脚本模拟用户操作

    ………… 那么你就可以来看看这篇帖子了. 言归正传 利用bat脚本模拟用户操作,需要用到两点: ①就是adb命令了,adb命令可以用来模拟用户在手机上的操作 ②bat语言,就是批处理语言,主要用来进行 ...

  6. 如何在 Azure 中自定义 Windows 虚拟机

    若要以快速一致的方式配置虚拟机 (VM),通常需要某种形式的自动化. 自定义 Windows VM 的一种常用方法是使用适用于 Windows 的自定义脚本扩展. 本教程介绍如何执行下列操作: 使用自 ...

  7. Jboss7.1 local EJB lookup problem

    We are trying to lookup for an Local EJB in JBoss7.1, but we get an ClassCast Exception. This local ...

  8. jboss eap6.1(5)(ejb升级)

    以前的项目是基于ejb2.x做的,ejb的配置文件为ejb-jar.xml和jboss.xml,现在把这个项目移到新版本服务器中的时候,报解析ejb-jar错误. 查阅许多资料才找到解决办法,原来jb ...

  9. 阿里八八Alpha阶段Scrum(6/12)

    今日进度 叶文滔: 修复了无法正确判断拖曳与点击的BUG,并且成功连接添加界面. 会议内容 会议照片 明日安排 叶文滔: 继续完善按钮功能 王国超: 继续攻克日程界面显示存在的BUG 俞鋆: 继续进行 ...

  10. 团队作业——Alpha冲刺 7/12

    团队作业--Alpha冲刺 冲刺任务安排 杨光海天 今日任务:将编辑界面与标题栏合并.与同队成员,讨论部分功能合并的问题. 明日任务:编辑界面与另一队员完成的字体设置弹窗合并. 郭剑南 今日任务:使用 ...