背景介紹
最近遇到了這樣一個問題,我們有一個 jar 包 common-tool,作爲基礎工具包,被各個項目在引用。突然某一天發現日誌很多報錯。
一看是 NoSuchMethodError,意思是 DisJunction 裏 init 方法沒找到,但是我檢查了代碼是有這個方法的啊。
問題定位
當時百度了一下,都說這種情況一般是 jar 包衝突了,但是本地看來下沒有 jar 包版本都一致,沒有衝突啊,百思不得其解。於是寫了個代碼看看這個報找不到方法的類到底有沒有這個方法。
Constructor<?>[] constructors = Disjunction.class.getConstructors();
for (Constructor constructor:constructors){
//查看類的構造器方法
log.error("find monitor bug:{}",constructor);
}
Class targetclass = Disjunction.class;//可以用自己想知道的類替換
String className = targetclass.getName();
className = className.replace('.', '/');
String resource = "/" + className + ".class";
URL url = targetclass.getResource(resource);
//查看類的全路徑
log.error("find monitor bug:{}",url.getFile());
打印出來後發現類全路徑竟然不是我的包裏的,是一個 agent.jar 裏面的。
問了下才知道原來是運維添加了一個 agent 到線上環境,裏面的 byte-buddy 依賴和我的衝突了。
如果是本地衝突的話,也可以使用 IDEA 裏的 Maven Helper 插件,它可以清晰的指出某個包被哪幾個包依賴。
解決方案:
在看解決方案之前先來回顧一下Maven的依賴原則:
- 最短路徑優先
即如果我 A->B->C->X1.0,D->E->X2.0,我項目裏引入了 A 和 D,那麼我的 X 版本是用的 2.0,因爲 X2.0 路徑最短。
- 申明順序優先
如果 A-B-X(1.0) ,A-C-X(2.0) 這樣的路徑長度一樣怎麼辦呢?這樣的情況下,maven 會根據 pom 文件聲明的順序加載,如果先聲明瞭 B,後聲明瞭 C,那就最後的依賴就會是 X(1.0)
解決方案 1:
maven 依賴的順序是路徑優先,所以我們可以在項目的 pom 文件裏直接申明版本,這樣就比項目裏的引入的 jar 包裏再引入的依賴優先級要高些。
這種方案的缺點就是因爲我這個基礎 jar 包好幾個項目再用,需要每個項目都要修改。
解決方案 2:
使用 maven 插件 maven-shade-plugin
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
//替換的範圍,只替換下面兩個包 <include>net.bytebuddy:byte-buddy</include>
<include>net.bytebuddy:byte-buddy-agent</include>
</includes>
</artifactSet>
<createSourcesJar>true</createSourcesJar>
<relocations>
<relocation>
// 將net.bytebuddy依賴重命名爲cn.mmc.net.bytebuddy
<pattern>net.bytebuddy</pattern>
<shadedPattern>cn.mmc.net.bytebuddy</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
這種做法就是將原有依賴的 jar 包都重命名一下,比如裏面的類是 net.bytebuddy.DisJunction,用這個插件後就變爲了 cn.mmc.net.bytebuddy.DisJunction,這樣類的全路徑不一樣,就徹底杜絕了依賴衝突的情況。
另外上面的 includes 是指僅指定替換某些依賴裏包名是 net.bytebuddy,否則所有包含了 net.bytebuddy 的都會被重命名,這樣打出來的 jar 包就會非常大。
總結
爲了一勞永逸,我使用了第二種方案 maven-shade-plugin 的方式,發佈之後依賴衝突的問題就解決了。