一招解決所有依賴衝突

背景介紹

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章