發(fā)生jar包沖突通常是因?yàn)椋?xiàng)目中依賴了同一個(gè)jar包的多個(gè)版本蝎抽。一般的思路是只保留一個(gè)版本政钟,刪除掉不需要的版本。最近遇到了一個(gè)下圖這樣的例子:
排掉d1的話a會(huì)報(bào)錯(cuò)樟结,排掉d2的話b會(huì)報(bào)錯(cuò)养交,所以希望在項(xiàng)目中同時(shí)使用d1和d2。
與前言中的圖相對(duì)應(yīng),項(xiàng)目總共分3個(gè)模塊:a\b\c,其中a和b分別依賴了guava的19.0(d1)和26.0.jre(d2),然后c同時(shí)引用了a和b瓢宦。
其中g(shù)uava的兩個(gè)版本有下邊兩個(gè)不兼容的方法碎连,用來(lái)測(cè)試:
public static Objects.ToStringHelper toStringHelper(Object self) {
//該方法 19.0有,26.0.jre沒(méi)有
}
public static String lenientFormat(
@Nullable String template, @Nullable Object @Nullable... args) {
//該方法 19.0沒(méi)有驮履,26.0.jre有
}
直接執(zhí)行com.zhaohui.C的main方法鱼辙,會(huì)報(bào)如下錯(cuò)誤:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Strings.lenientFormat(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
接下來(lái)就介紹一下解決問(wèn)題的工具maven-shade-plugin廉嚼。(源碼地址:github) 這里使用3.2.1的tag。
為了更簡(jiǎn)單的了解maven-shade-plugin這個(gè)插件到底做了什么倒戏,可以直接打斷點(diǎn)調(diào)試一下怠噪,跟著源碼走一遍打包流程。
首先杜跷,下載源碼傍念,然后添加到package-test的module中,具體操作如下圖:
然后package-test-c項(xiàng)目的pom文件增加這個(gè)插件:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.zhaohui.C</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
mvn插件的代碼入口是@Mojo注解類的execute方法葱椭,在該方法入口(org.apache.maven.plugins.shade.mojo.ShadeMojo line385)打個(gè)斷點(diǎn)捂寿,如下圖:
最后一步按照下圖操作:
增加一個(gè)run/debug configuration 最后點(diǎn)擊debug按鈕就可以調(diào)試了。具體生成jar包的代碼org.apache.maven.plugins.shade.DefaultShader line151 shadeJars方法孵运。
打斷點(diǎn)發(fā)現(xiàn)秦陋,打包的時(shí)候,解析pom文件總共獲取了4個(gè)jar包治笨,其中g(shù)uava只有19.0驳概,沒(méi)有26.0.jre,所以執(zhí)行的時(shí)候才會(huì)報(bào)錯(cuò)找不到26.0.jre中的方法旷赖。(maven選擇時(shí)只能允許一個(gè)guava jar包顺又,放在classpath里面)
調(diào)試中,在org.apache.maven.plugins.shade.DefaultShader line539 有下面代碼:
sourceContent = relocator.applyToSourceContent( sourceContent );
這個(gè)relocator會(huì)在打包過(guò)程中等孵,修改類的包名稚照。這個(gè)就是解決這個(gè)問(wèn)題的關(guān)鍵。具體解決思路如下圖:
在項(xiàng)目中新增一個(gè)模塊b-shade,里邊什么代碼都沒(méi)有俯萌,只有一個(gè)dependency b果录,然后配置maven-shade-plugin 如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>zhaohui.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
要求b-shade使用maven-shade-plugin打包,同時(shí)打包的時(shí)候規(guī)定將com.google.common包名改為zhaohui.com.google.common咐熙。
然后在c的pom文件中刪除b的依賴弱恒,改為依賴b-shade.然后在根目錄執(zhí)行mvn package。
最后棋恼,在c的target目錄中執(zhí)行java -jar package-test-c-1.0-SNAPSHOT.jar,輸出如下:
開(kāi)始執(zhí)行A的代碼
Object{test=test}
A的代碼,執(zhí)行完了,沒(méi)報(bào)錯(cuò)
開(kāi)始執(zhí)行A的代碼
[java.lang.Object@330bedb4]
B的代碼,執(zhí)行完了,沒(méi)報(bào)錯(cuò)
這樣問(wèn)題就圓滿解決了返弹。
最后的最后,使用luyten-0.5.3打開(kāi)package-test-c-1.0-SNAPSHOT.jar爪飘,發(fā)現(xiàn)b中的import語(yǔ)句已經(jīng)被修改為import zhaohui.com.google.common.base.*;
但是debug還是不行义起, 這涉及到idea debug機(jī)制問(wèn)題(根據(jù)dependency設(shè)置的classpath, 只選擇了guava19)师崎,這里就不具體展開(kāi)說(shuō)明了并扇。