发生jar包冲突通常是因为,项目中依赖了同一个jar包的多个版本。一般的思路是只保留一个版本,删除掉不需要的版本。最近遇到了一个下图这样的例子:
排掉d1的话a会报错,排掉d2的话b会报错,所以希望在项目中同时使用d1和d2。
与前言中的图相对应,项目总共分3个模块:a\b\c,其中a和b分别依赖了guava的19.0(d1)和26.0.jre(d2),然后c同时引用了a和b。
其中guava的两个版本有下边两个不兼容的方法,用来测试:
public static Objects.ToStringHelper toStringHelper(Object self) {
//该方法 19.0有,26.0.jre没有
}
public static String lenientFormat(
@Nullable String template, @Nullable Object @Nullable... args) {
//该方法 19.0没有,26.0.jre有
}
直接执行com.zhaohui.C的main方法,会报如下错误:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Strings.lenientFormat(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
接下来就介绍一下解决问题的工具maven-shade-plugin。(源码地址:github) 这里使用3.2.1的tag。
为了更简单的了解maven-shade-plugin这个插件到底做了什么,可以直接打断点调试一下,跟着源码走一遍打包流程。
首先,下载源码,然后添加到package-test的module中,具体操作如下图:
然后package-test-c项目的pom文件增加这个插件:
<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)打个断点,如下图:
最后一步按照下图操作:
增加一个run/debug configuration 最后点击debug按钮就可以调试了。具体生成jar包的代码org.apache.maven.plugins.shade.DefaultShader line151 shadeJars方法。
打断点发现,打包的时候,解析pom文件总共获取了4个jar包,其中guava只有19.0,没有26.0.jre,所以执行的时候才会报错找不到26.0.jre中的方法。(maven选择时只能允许一个guava jar包,放在classpath里面)
调试中,在org.apache.maven.plugins.shade.DefaultShader line539 有下面代码:
sourceContent = relocator.applyToSourceContent( sourceContent );
这个relocator会在打包过程中,修改类的包名。这个就是解决这个问题的关键。具体解决思路如下图:
在项目中新增一个模块b-shade,里边什么代码都没有,只有一个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打包,同时打包的时候规定将com.google.common包名改为zhaohui.com.google.common。
然后在c的pom文件中删除b的依赖,改为依赖b-shade.然后在根目录执行mvn package。
最后,在c的target目录中执行java -jar package-test-c-1.0-SNAPSHOT.jar,输出如下:
开始执行A的代码
Object{test=test}
A的代码,执行完了,没报错
开始执行A的代码
[java.lang.Object@330bedb4]
B的代码,执行完了,没报错
这样问题就圆满解决了。
最后的最后,使用luyten-0.5.3打开package-test-c-1.0-SNAPSHOT.jar,发现b中的import语句已经被修改为import zhaohui.com.google.common.base.*;
但是debug还是不行, 这涉及到idea debug机制问题(根据dependency设置的classpath, 只选择了guava19),这里就不具体展开说明了。