转载自知乎专栏:https://zhuanlan.zhihu.com/p/62796806
前言:
发生jar包冲突通常是因为,项目中依赖了同一个jar包的多个版本。一般的思路是只保留一个版本,删除掉不需要的版本。最近遇到了一个下图这样的例子:
排掉d1的话a会报错,排掉d2的话b会报错,所以希望在项目中同时使用d1和d2。
最开始同事说可以用maven-shade-plugin这个插件实现这个需求的时候,我的内心是这样的:
后来听他解释完以后才恍然大悟,感觉世界观再一次被刷新了,居然还有这种操作。。。下面就简单描述一下具体怎么用shade插件解决这个问题的吧。
正文:
首先介绍一下具体测试代码: (下载链接:package-test)
与前言中的图相对应,项目总共分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文件增加这个插件:
<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>
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中的方法。
调试中,在org.apache.maven.plugins.shade.DefaultShader line539 有下面代码:
sourceContent = relocator.applyToSourceContent( sourceContent );
这个relocator会在打包过程中,修改类的包名。这个就是解决这个问题的关键。具体解决思路如下图:
在项目中新增一个模块b-shade,里边什么代码都没有,只有一个dependency b,然后配置maven-shade-plugin 如下:
<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.*;
总结:
我只想说:新技能get√
最后,让我们保持独立思考,不卑不亢。长成自己想要的样子! (引用自 我非常喜欢的B站up主 ”独立菌儿“->猛戳链接<-的口头禅)
评论 (0)