maven插件之maven-shade-plugin

发布时间 2023-06-30 16:26:21作者: 黄河大道东

官网:https://maven.apache.org/plugins/maven-shade-plugin/index.html

  Apache maven shade plugin提供把工程的artifact及其依赖打包到一个uber-jar中并能隐藏起来(比如重命名),shade插件仅仅有一个功能就是创建一个shaded包。
  那什么是uber-jar,uber是德语,这里表示把所有的依赖都定义到一个jar文件里。

  • 插件支持两种操作includeexclude
  • 配置格式:groupId:artifactId[[:type]:classifier],至少包含groupid和artifactid,type和分类器
    • 例如com.example:my-project:jar:1.0-SNAPSHOT:tests使用组织为 "com.example",项目为 "my-project",类型为 "jar",版本为 "1.0-SNAPSHOT",分类器为 "tests" 的依赖项。
  • 支持*’和 ?执行通配符匹配

排除包级别的依赖

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <configuration>
                <!-- 此处按需编写更具体的配置 -->
                <artifactSet>
                    <excludes>
                        <!-- 排除junit组织的junit项目 -->
                        <exclude>junit:junit</exclude>
                        <!-- 排除jmock组织的所有项目 -->
                        <exclude>jmock:*</exclude>
                        <!-- 排除所有组织的名字叫log4j的项目 -->
                        <exclude>*:log4j</exclude>
                    </excludes>
                </artifactSet>
            </configuration>
            <executions>
                <execution>
                    <!-- 和 package 阶段绑定 -->
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

排除文件级别的配置

可处理类与资源文件

<configuration>
    <filters>
        <filter>
            <!-- junit组织的junit项目参与过滤 -->
            <artifact>junit:junit</artifact>
            <includes>
                <!-- 包含以下路径的所有文件 -->
                <include>junit/framework/**</include>
                <include>org/junit/**</include>
            </includes>
            <excludes>
                <!-- 排除以下路径的所有文件 -->
                <exclude>org/junit/experimental/**</exclude>
                <exclude>org/junit/runners/**</exclude>
            </excludes>
        </filter>
        <filter>
            <!-- 所有依赖参与过滤 -->
            <artifact>*:*</artifact>
            <excludes>
                <!-- 排除所有依赖下的这些文件 -->
                <exclude>META-INF/*.SF</exclude>
                <exclude>META-INF/*.DSA</exclude>
                <exclude>META-INF/*.RSA</exclude>
            </excludes>
        </filter>
    </filters>
</configuration>

重定位类文件

  如果最终的 jar 包被其他的项目所依赖的话,直接地引用此 jar 包中的类可能会导致类加载冲突,这是因为 classpath 中可能存在重复的 class 文件。为了解决这个问题,可以使用 shade 提供的重定位功能,把部分类移动到一个全新的包中。示例如下:

<configuration>
    <relocations>
        <relocation>
            <!-- 原始包名 -->
            <pattern>org.a</pattern>
            <!-- 重命名后的包名 -->
            <shadedPattern>org.b</shadedPattern>
            <!-- 原始包内不需要重定位处理的类,类名支持通配符 -->
            <excludes>
                <exclude>org.a.Xpp3Dom</exclude>
                <exclude>org.a.pull.*</exclude>
            </excludes>
        </relocation>
    </relocations>
</configuration>
<!-- 
含义:把 org.a 包内的所有子包及 class 文件(除了 org.a.Xpp3Dom 和 org.a.pull 包下的所有 class 文件)重定位到 org.b 包内
-->

  如果包内的大部分类都不需要,一个个排除就显得很繁琐了。此时可以使用 <includes> 标签来指定仅需要处理的类,示例如下:

<configuration>
    <relocations>
        <relocation>
            <!-- 原始包名 -->
            <pattern>org.a</pattern>
            <!-- 重命名后的包名 -->
            <shadedPattern>org.b</shadedPattern>
            <!-- 原始包内需要重定位处理的类,类名支持通配符 -->
            <includes>
                <include>org.a.io.*</include>
            </includes>
        </relocation>
    </relocations>
</configuration>
<!-- 
含义:把 org.a 包内的 org.a.io 包下的所有 class 文件重定位到 org.b 包内,其它的都不做处理
-->

生成可执行的jar

<configuration>
    <transformers>
        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>入口类</mainClass>
        </transformer>
    </transformers>
</configuration>

  熟悉 jar 包的都知道,jar 包中默认会包含一个 MANIFEST.MF 文件,里面描述了一些 jar 包的信息。使用 java 自带的 jar 命令打包的时候可以指定 MANIFEST.MF,其中也可以指定 Main-Class 来使得 jar 包可运行。那么使用 shade 来指定和直接在 MANIFEST.MF 文件中指定没有区别,细心会发现 <mainClass> 标签的父标签是 <transformer> 有一个 implementation 属性,其值为 org.apache.maven.plugins.shade.resource.ManifestResourceTransformer,意思是 Manifest 资源文件转换器。上述示例只自指定了启动类入口,因此 shade 会为我们自动生成一个包含 Main-Class 的 MANIFEST.MF 文件,然后在打 jar 包时指定这个文件。

  想要完全定制 MANIFEST.MF 文件内容就可以使用 <manifestEntries> 标签,示例如下:

<configuration>
    <transformers>
        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <manifestEntries>
                <Main-Class>启动类入口</Main-Class>
                <Build-Number>123</Build-Number>
            </manifestEntries>
        </transformer>
    </transformers>
</configuration>

  可执行jar包通过 java -jar 命令启动这个jar包

生成资源文件

使用方法:https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html

  项目中涉及到的依赖可能会有它们所必需的资源文件,使用 shade 可以把它们聚合在同一个 jar 包中。默认地,shade 提供了 12 个 ResourceTransformer 类:

类名 作用
ApacheLicenseResourceTransformer 防止 LICENSE 文件重复
ApacheNoticeResourceTransformer 准备合并的 NOTICE
AppendingTransformer 为某个资源文件附加内容
ComponentsXmlResourceTransformer 聚合 Plexus components.xml
DontIncludeResourceTransformer 防止包含指定的资源
GroovyResourceTransformer 合并 Apache Groovy 的扩展模块
IncludeResourceTransformer 添加项目中的文件为资源文件
ManifestResourceTransformer 自定义 MANIFEST 文件
PluginXmlResourceTransformer 聚合 Maven 的 plugin.xml 配置
ResourceBundleAppendingTransformer 合并 ResourceBundles
ServicesResourceTransformer 重定位且合并 META-INF/services 资源文件中的 class 文件.
XmlAppendingTransformer 为 XML 资源文件附加内容

如果上述 12 个类都不能够满足我们的需求,我们可以实现 shade 提供的接口,按需自定义一个 ResourceTransformer,实现方法详见官网 Using your own Shader implementation

参考文章:

https://blog.csdn.net/u011441473/article/details/127844885