jar依赖冲突解决实践

jar依赖冲突解决实践


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

Maven构建的应用的jar冲突

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

1

2

3

4

5

6

7com.taobao.diamonddiamond-client3.6.0jarcompile

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

[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

7org.antlrantlr3.3jarcompile

依赖仲裁

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

maven 2.2.1版本仲裁规则:

按照项目总POM的DependencyManager版本声明进行仲裁(覆盖),但无警告

如无仲裁声明,则按照依赖最短路径确定版本

若相同路径,有严格区间限定的版本优先

若相同路径,无版本区间,则按照先入为主原则

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

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

maven classpath

maven中有三种classpath:

编译classpath:编译项目代码,依赖的jar会被引入到classpath

测试classpath:编译和执行测试部分代码,如单元测试,集成测试,依赖会被引入到classpath

运行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->Ccompileprovidedtestruntime

compilecompileruntime

providedprovidedprovidedprovided

testtesttest

runtimeruntimeruntime

可选依赖

1

2

3

4

5

6

7

8org.antlrantlr3.3jarcompiletrue

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[Loadedcom.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][Loadedcom.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

00007fa4ae17400020K 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工程,则通过其他方式把错误引用排除掉即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容