`
fufeng
  • 浏览: 73881 次
社区版块
存档分类
最新评论

如何解决包冲突问题

阅读更多

随着业务需求的不断扩展,应用中代码量也会逐渐增长,工程中引用的二方包或者三方包也自然而然会越来越多。因此,不可避免,可能存在引用的二方包或三方包相互冲突所导致的系统问题。

 

本文将针对前段时间遇到的实际案例进行分析,旨在当遇到包冲突问题时该如何解决,并提供同事用 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,重新编译工程,启动,问题解决。

 

分享到:
评论
4 楼 qszine377 2017-03-06  
遇到同样的问题了,文章写得很清晰,但是没有提供conflictdetect.exe的下载,现在还是很困扰
3 楼 lly8787 2017-03-02  
图怎么看????
2 楼 qja 2015-05-25  
conflictdetect.exe 能否发我一下?
1 楼 gzg0141 2014-09-02  
你好,你的conflictdetect工具可否共享一下,不胜感谢。

邮箱:75123437@qq.com

相关推荐

Global site tag (gtag.js) - Google Analytics