maven-shade-plugin relocation解决冲突

发生jar包冲突通常是因为,项目中依赖了同一个jar包的多个版本。一般的思路是只保留一个版本,删除掉不需要的版本。最近遇到了一个下图这样的例子:


image.png

排掉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中,具体操作如下图:


image.png

image.png

然后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)打个断点,如下图:


image.png

最后一步按照下图操作:


image.png

增加一个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会在打包过程中,修改类的包名。这个就是解决这个问题的关键。具体解决思路如下图:


image.png

在项目中新增一个模块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.*;


image.png
image.png

但是debug还是不行, 这涉及到idea debug机制问题(根据dependency设置的classpath, 只选择了guava19),这里就不具体展开说明了。


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。