Maven Shade 插件替换 Calcite Jar 包 class 文件

由于项目中直接通过 Copy Calcite Jar 包内部的 Java 文件的方式进行代码修改,在运行 ShardingSphere Proxy 过程中,ShardingSphere 的 Jar 包和第三方 Jar 包都放在 lib 目录下,类加载器加载类时,可能会随机加载到 calcite 原有 Jar 包里的同名类,导致类覆盖修改失效。

此处使用 Maven Shade Plugin 在打包阶段将项目中自定义的同名类,替换掉 Calcite Jar 包里的类,实现定制代码。

Maven Shade 插件功能

官网介绍了如下几种使用方式:

  1. 将多个 Jar 包合并成一个 all in one Jar 包
    通过 artifactSet 配置要打包哪些 Jar 为一个 all in one Jar:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <configuration>
    <artifactSet>
    <includes>
    <include>classworlds:classworlds</include>
    <include>junit:junit</include>
    </includes>
    <excludes>
    <exclude>jmock:*</exclude>
    <exclude>org.apache.maven:lib:tests</exclude>
    </excludes>
    </artifactSet>
    </configuration>

通过 filters 标签配置对指定的 artifact 里的类进行 include 和 exclude 操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<configuration>
<filters>
<filter>
<artifact>junit:junit</artifact>
<includes>
<include>junit/framework/**</include>
</includes>
<excludes>
<exclude>org/junit/experimental/**</exclude>
</excludes>
</filter>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
</excludes>
</filter>
</filters>
</configuration>
  1. 通过 relocations 标签将包名 shade,避免依赖冲突

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <configuration>
    <relocations>
    <relocation>
    <pattern>org.codehaus.plexus.util</pattern>
    <shadedPattern>org.shaded.plexus.util</shadedPattern>
    <excludes>
    <exclude>org.codehaus.plexus.util.xml.pull.*</exclude>
    </excludes>
    </relocation>
    </relocations>
    </configuration>
  2. 通过 transformer 处理 MANIFEST
    此处通过 ManifestResourceTransformer 实现类,自定义 Jar 包的 mainClass 启动类。

    1
    2
    3
    4
    5
    6
    7
    <configuration>
    <transformers>
    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    <mainClass>org.sonatype.haven.HavenCli</mainClass>
    </transformer>
    </transformers>
    </configuration>

替换 Calcite Jar 包里的类

很显然,要实现项目中定制 Calcite Jar 包里的类,采用第一种方式即可,配置示例如下:
以下配置将 calcite-core 和 federation-compiler 两个 Jar 包 shade 成一个 Jar 包,使用 federation-compiler 类自定义的 calcite 实现类覆盖原有 calcite Jar 包里的实现类,最终打包后的包名仍然使用 calcite-core.jar 实现类覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
<configuration>
<artifactSet>
<includes>
<include>org.apache.calcite:calcite-core</include>
<include>org.apache.shardingsphere:shardingsphere-sql-federation-compiler</include>
</includes>
</artifactSet>
<filters>
<filter>
<artifact>org.apache.calcite:calcite-core</artifact>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>org/apache/calcite/rex/RexCallBinding.class</exclude>
<exclude>org/apache/calcite/rex/RexCallBinding$*.class</exclude>
<!-- ...... -->
</excludes>
</filter>
<filter>
<artifact>org.apache.shardingsphere:shardingsphere-sql-federation-compiler</artifact>
<includes>
<include>org/apache/calcite/**</include>
</includes>
</filter>
</filters>
<outputFile>${project.build.directory}/lib/calcite-core-${calcite.version}.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>

需要注意的点,比如原有 Calcite 里 RexCallBinding.java 里具有内部类,最终编译后的 Jar 包里会包含 RexCallBinding.class 和 RexCallBinding$1.class 两个 class 文件,需要同时排除掉,避免后续 Jar 包中的类异常。

替换完毕后,可以用二进制比对工具或者 Java 反编译工具,对比下最终生成所有 class 文件是否符合预期。

参考

Maven Shade Plugin Demos