背景介紹
最近遇到了這樣一個問題,我們有一個 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 的方式,釋出之後依賴衝突的問題就解決了。