随着业务需求的不断扩展,应用中代码量也会逐渐增长,工程中引用的二方包或者三方包也自然而然会越来越多。因此,不可避免,可能存在引用的二方包或三方包相互冲突所导致的系统问题。
本文将针对前段时间遇到的实际案例进行分析,旨在当遇到包冲突问题时该如何解决,并提供同事用 python 写的一个发现包冲突的小工具(十分有用!)
一 发现问题:
首先 ,让我们看下异常,这是在应用启动后,执行具体操作时所报的错误:
Caused by: java.lang.NoSuchMethodError: com.google.common.collect.MapMaker.expireAfterWrite(JLjava/util/concurrent/TimeUnit;)Lcom/google/common/collect/MapMaker;
at com.taobao.treasure.client.TreasureClientImpl.<init>(TreasureClientImpl.java:31)
at com.taobao.treasure.client.TreasureClientFactory$1.getPipeline(TreasureClientFactory.java:95)
at org.jboss.netty.bootstrap.ClientBootstrap.co
nnect(ClientBootstrap.java:212)
at org.jboss.netty.bootstrap.ClientBootstrap.connect(ClientBootstrap.java:188)
at com.taobao.treasure.client.TreasureClientFactory$2.call(TreasureClientFactory.java:261)
at com.taobao.treasure.client.TreasureClientFactory$2.call(TreasureClientFactory.java:248)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at com.taobao.treasure.client.TreasureClientFactory.getClient(TreasureClientFactory.java:239)
... 53 more
根据错误我们大概可以了解,工程中引用了一个名叫 com.google.common.collect.MapMaker 的类,并调用了其中名为 expireAfterWrite() 的方法,但是系统在加载该方法时却表示没有找到该方法。
这是一个很明显的包冲突问题,这类情况的发生很可能是因为如下状况引起的:
如图,你引用了 2 个三方包 a.jar 和 b.jar , a.jar 中又引用了一个 c.jar ,假设 c.jar 的版本号为 version-1, b.jar 中也引用了 c.jar ,假设这里的 c.jar 相对于 a.jar 中的 c.jar 为较高版本,记为 version-2 , b.jar 中某个类引用了 c.jar 的类 classA 中的方法 method A() ,并且该方法只存在于高版本的 c.jar(version-2) 的类 classA 中,而不存在 c.jar(version-1) 的类 classA 中。
当系统编译加载时,系统可能编译加载 c.jar(version-1) ,也可能编译加载 c.jar(version-2) ,当编译加载 c.jar(version-2) 时,由于很多 jar 包都支持向下兼容,即高版本兼容低版本,因此不论 a.jar 调用 c.jar 还是 b.jar 调用 c.jar 一般都不会出问题。但如果此时刚好应用编译加载的是 c.jar(version-1) 中的类 classA 时,那么 b.jar 调用 Method A() 时便会报上述错误,因为 Method A() 函数只存在于高版本的 c.jar 中,而此时系统编译加载的却是低版本的 c.jar 。
二 解决问题
当遇到这类问题我们该如何解决呢 ? 主要有以下三步:
第一, 发现是哪个类发生了冲突;
第二, 发现冲突 jar 包,即冲突类存在于哪个 Jar 包中;
第三, 发现这个冲突 Jar 包是自身系统直接引用的还是系统引用的 Jar 间接引用的。
针对上述第一步,我们使一个 用 python 写的 名为 conflictdetect 小工具来解决。
将 conflictdetect.exe 下载后,存放到某个目录,然后将 conflictdetect.exe 的存放路径设定至环境变量 path 中,打开 CMD ,到 jar 包所在目录。
运行:
conflictdetect.exe
就会检测到目录中存在冲突的 jar 包,并以三种格式输出 ,: 只输出 jar ,只输出 class, 两个都输出。
通过 conflictdetect –h 可以查看用法。
本人将 conflictdect.exe 放置于 D:\exe.win32-3.2 下,因此在环境变量 Path 中设置的是 D:\exe.win32-3.2 ,打开 CMD ,定位到应用存放 jar 包的目录下,执行 :
conflictdetect.exe –t –f –o “D:/out.log”
此时,在 D 盘中将会发现一个名为 out.log 的文档,里面记录了存放 jar 包目录下所有冲突的 class 和 jar 。在其中我们 search 一把上述系统冲突的类名“ MapMaker ”,发现他们原来是存在于 google-collections-1.0.jar' 和 'guava-r09.jar' 中。 如下图:
org/springframework/remoting/jaxrpc/JaxRpcPortProxyFactoryBean.class=['spring-2.0.7.jar', 'spring-remoting-1.2.7.jar']
com/alibaba/service/uribroker/DefaultURIBrokerService.class=['toolkit-service-uribroker-1.0.jar', 'toolkit-webx-all-in-one-2.0.jar']
com/alibaba/service/pool/RecyclableSupport.class=['toolkit-service-pool-1.0.jar', 'toolkit-webx-all-in-one-2.0.jar']
com/google/common/collect/MapMaker$1.class=['google-collections-1.0.jar', 'guava-r09.jar']
org/springframework/core/task/SimpleAsyncTaskExecutor.class=['spring-2.0.7.jar', 'spring-core-2.0.6.jar']
此时,对系统有影响的冲突类和冲突 jar 包我们都已经发现了。
通过网上百度,原来 google-collections-1.0.jar 和 guava.jar 都 google 的产品,并且 guava.jar 是 google-collections-1.0.jar 升级版本,因此在编译过程中我们应该把 guava.jar 编译进工程,而不能把 google-collections-1.0.jar 编译进工程。
第三步,我们看 google-collections-1.0.jar 是否是应用直接引用的。经过确认,我的项目中并没有直接引用这两个 jar 包,因此可能是通过其他 jar 包间接引用进来的。由于项目中是通过 maven 进行引用 jar 包的管理。因此, 结合 maven 的命令 mvn dependency:tree 可以很容易发现这两个jar 包到底是通过哪些jar 包间接引用进来的。
+- com.know.diamond:diamond-sdk:jar:2.0.5:compile
[INFO] | +- net.sourceforge.htmlunit:htmlunit:jar:1.14:compile
[INFO] | | +- rhino:js:jar:1.6R7:compile
[INFO] | | +- nekohtml:nekohtml:jar:0.9.5:compile
[INFO] | | \- net.sourceforge.cssparser:cssparser:jar:0.9.4:compile
[INFO] | +- com.taobao.diamond:diamond-utils:jar:2.0.5:compile
[INFO] | | +- org.codehaus.jackson:jackson-core-lgpl:jar:1.4.0:compile
[INFO] | | \- org.codehaus.jackson:jackson-mapper-lgpl:jar:1.4.0:compile
[INFO] | \- com.google.collections:google-collections:jar:1.0:compile
原来是 diamond-sdk:jar 间接引用了 com.google.collections:google-collections:jar. 因此我们通知 diamond-sdk:jar 的维护者进行 jar 包升级,或者在 MVN 编译过程中强制禁止 google-collections:jar 编译进工程。如下:
<dependency>
<groupId>com.know.diamond</groupId>
<artifactId>diamond-sdk</artifactId>
<version>2.0.5</version>
<exclusions>
<exclusion>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
OK,重新编译工程,启动,问题解决。
分享到:
相关推荐
SSH包冲突问题解决SSH包冲突问题解决_sqlserverSSH包冲突问题解决 包冲突问题解决
在websphere8.5 下部署含有CXFwebservice的war包无法正常启动,...归根结底原因不是找不到类,而是发布后war下的jar与websphere自身平台下的jar冲突了,要解决此问题就要通过在websphere下建立共享库来根除,可参考附件
因与cxf包xmlschema-core-*.jar 冲突 故重新打包 XmlSchema-1.1.jar 源码修改原类径的方法来重新打包避免冲突 源码放在javas.rar文件夹中 org.codehaus.xfire.wsdl11.parser.SchemaInfo org.codehaus.xfire....
该文档用于解决在websphere application server 服务部署程序过程中会出现jar包冲突问题
实际开发中,可以采用多种方法来解决包冲突问题,比较常见的是类似 SpringBoot 的做法,统一管理应用所有依赖包的版本,保证这些三方包不存在依赖冲突。这种做法只能有效避免包冲突的问题,不能根本上解决包冲突的...
SSH整合jar包,彻底解决集成jar包冲突问题,内涵绝大部分常用框架的jar包和一些常用jar包
解决jar包类名冲突问题
实际开发中,可以采用多种方法来解决包冲突问题,比较常见的是类似 SpringBoot 的做法,统一管理应用所有依赖包的版本,保证这些三方包不存在依赖冲突;这种做法只能有效避免包冲突的问题,不能根本上解决包冲突的...
因与cxf包xmlschema-core-*.jar 冲突 故重新打包 XmlSchema-1.1.jar 源码修改原类径的方法来重新打包避免冲突 源码放在javas.rar文件夹中 org.codehaus.xfire.wsdl11.parser.SchemaInfo org.codehaus.xfire....
Maven的Jar包冲突问题,经常出现的场景为: 本地运行报NoSuchMethodError,ClassNotFoundException。明明在依赖里有这个Jar包啊。怎么运行不了!? 项目中明明定义着某个jar包版本为2.0.2,怎么打包之后变成2.5.0了...
jstl Jar包 以及版本冲突问题解决 内附两个版本的Jar包 以及版本冲突解决方法
Jar包冲突问题及解决方案.docx
压缩包里面有myself工程。里面只有一个类。可以自己创建两个版本的myself的jar包,然后测试osgi解决jar包冲突。
冲突的问题,会导致CB不能正常关闭, 文本编辑时,右键菜单程序崩溃等问题。 可以通过插件管理,禁用EditorTweaks 这样可以正常使用,中文语言包 中文语言包正确安装位置 例如: D:\codeblocks\share\CodeBlocks\...
导入jar出现包冲突,可以删除其中一个来解决。都想保留的话可以通过工具修改包名来解决。以gson为例修改包名,可供参考,欢迎指正。
解决jar包冲突的工具类jarjar-1.4.rar,jar包冲突解决方法,解决jar与jar冲突,jar与aar冲突,不想删除任何一个包,只能修改其中一个jar包包名即可解决,解决方法详细请看这篇文章: ...
easyExcel支持最低的POI版本是3.17,现有项目若已用POI版本比这高的话,希望不改变现有的引用,解决jar包冲突的话,使用该独立版的jar包。该包已改过包名,亲测可兼容使用,网上有说把poi升级到4.1.2但是试了导出...