本文翻译源于Apache Maven Project 官网
控制哪些jar包应该包含在项目jar中,哪些不包含
[原文] The POM snippet below shows how to control which project dependencies should be included/excluded in the uber JAR:
<project>
...
<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>
<artifactSet>
<excludes><!--去掉这些jar-->
<exclude>classworlds:classworlds</exclude>
<exclude>junit:junit</exclude>
<exclude>jmock:*</exclude>
<exclude>*:xml-apis</exclude>
<exclude>org.apache.maven:lib:tests</exclude>
<exclude>log4j:log4j:jar:</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
使用<includes>设置“白名单”
[原文] Of course, <includes> can be used as well to specify a white list of artifacts. Artifacts are denoted by a composite idenitifer of the form groupId:artifactId[[:type]:classifier]. Since plugin version 1.3, the wildcard characters '*' and '?' can be used to do glob-like pattern matching.
For fine-grained control of which classes from the selected dependencies are included, artifact filters can be used:
<project>
...
<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>
<filters>
<filter>
<artifact>junit:junit</artifact>
<includes><!-- 排除junit依赖的同时,保留以下文件 -->
<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>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
最小包
除了使用filters,还可以配置插件自动的去掉项目没有用到的classes,使用关键字<minimizeJar>
[原文] Besides user-specified filters, the plugin can also be configured to automatically remove all classes of dependencies that are not used by the project, thereby minimizing the resulting uber JAR
maven-shade-plugin插件可以配置自动精简那些项目没有用到的依赖的artifact,或者显式的配置精简项目用到的依赖的artifact下面没有用到的classes。
<project>
...
<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>
<minimizeJar>true</minimizeJar><!-- 精简掉不需要的dependency -->
<filters><!-- 精简掉 artifacts 下面项目没有用到的classes -->
<filter>
<artifact>log4j:log4j</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>commons-logging:commons-logging</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
配置后执行mvn package
打包,日志会出现以下提示:
...
[INFO] Minimizing jar com.hello:TCP-client:jar:1.0-SNAPSHOT
[INFO] Minimized 1266 -> 755 (59%)
...
重定位classes
Java 工程经常会遇到第三方 Jar 包冲突,使用 maven shade plugin 解决 jar 或类的多版本冲突。 maven-shade-plugin 在打包时,可以将项目中依赖的 jar 包中的一些类文件打包到项目构建生成的 jar 包中,在打包的时候把类重命名。下面的配置将 org.codehaus.plexus.util jar 包重命名为 org.shaded.plexus.util。
原文
If the uber JAR is reused as a dependency of some other project, directly including classes from the artifact's dependencies in the uber JAR can cause class loading conflicts due to duplicate classes on the class path. To address this issue, one can relocate the classes which get included in the shaded artifact in order to create a private copy of their bytecode:
<project>
...
<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>org.codehaus.plexus.util</pattern><!-- 原位置 -->
<shadedPattern>org.shaded.plexus.util</shadedPattern><!-- 重新定位的位置 -->
<excludes><!-- 不包含的classes -->
<exclude>org.codehaus.plexus.util.xml.Xpp3Dom</exclude>
<exclude>org.codehaus.plexus.util.xml.pull.*</exclude>
</excludes>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
我们同样可以使用<includes>
<project>
...
<relocation>
<pattern>org.codehaus.plexus.util</pattern>
<shadedPattern>org.shaded.plexus.util</shadedPattern>
<includes>
<include>org.codehaud.plexus.util.io.*</include>
</includes>
</relocation>
...
</project>
为uber jar增加后缀
<project>
...
<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>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jackofall</shadedClassifierName> <!-- 后缀名 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
打包可执行的 uber jar
<project>
...
<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>org.sonatype.haven.HavenCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
shade Resource Transformers 资源转换器
上面我们用到了<transformer>,transformer就是资源转换器。没有重叠的情况下,把多个 artifacts 聚合到uber jar 是非常简单的。当需要逻辑去聚合来自于多个资源的 jar,就是Resource Transformer发挥作用的地方。
以下是资源转换器的列表:
Transformers in org.apache.maven.plugins.shade.resource
Transformer | 作用 |
---|---|
ApacheLicenseResourceTransformer | Prevents license duplication |
ApacheNoticeResourceTransformer | Prepares merged NOTICE |
AppendingTransformer | Adds content to a resource |
ComponentsXmlResourceTransformer | Aggregates Plexus components.xml |
DontIncludeResourceTransformer | Prevents inclusion of matching resources |
GroovyResourceTransformer | Merges Apache Groovy extends modules |
IncludeResourceTransformer | Adds files from the project |
ManifestResourceTransformer | Sets entries in the MANIFEST |
PluginXmlResourceTransformer | Aggregates Mavens plugin.xml |
ResourceBundleAppendingTransformer | Merges ResourceBundles |
ServicesResourceTransformer | Relocated class names in META-INF/services resources and merges them. |
XmlAppendingTransformer | Adds XML content to an XML resource |
下面一一介绍作用
ManifestResourceTransformer
The ManifestResourceTransformer allows existing entries in the MANIFEST to be replaced and new entries added.
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${app.main.class}</Main-Class>
<X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
<X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
AppendingTransformer、XmlAppendingTransformer和ResourceBundleAppendingTransformer
使用AppendingTransformer、XmlAppendingTransformer和ResourceBundleAppendingTransformer合并特定文件的内容
有些jar包含具有相同文件名的额外资源(例如*.properties)。为了避免覆盖,您可以选择通过将它们的内容附加到一个文件中来合并它们。一个很好的例子是同时聚合spring-context 和 plexus-spring jars.。它们都有META-INF/spring.handlers 文件,Spring使用该文件处理XML模式名称空间。您可以使用如下所示的AppendingTransformer合并所有具有该特定名称的文件的内容:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
对于xml文件,你可以使用 XmlAppendingTransformer
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/magic.xml</resource>
<!-- Add this to enable loading of DTDs
<ignoreDtd>false</ignoreDtd>
-->
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
由于插件版本1.3.1,XmlAppendingTransformer默认情况下不加载dtd,因此避免了网络访问。这种模式的潜在缺点是无法解析外部实体,从而导致转换失败,例如,当使用某些JRE 1.4中使用的Crimson XML解析器时。如果转换后的资源使用外部实体,则可以重新启用DTD分辨率,或者将对xerces:xercesImpl:2.9.1的插件依赖项添加到POM中。
对于resourcebundle属性文件,您可以使用ResourceBundleAppendingTransformer,它也会考虑所有可用的地区:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ResourceBundleAppendingTransformer">
<!-- the base name of the resource bundle, a fully qualified class name -->
<basename>path/to/Messages</basename>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
DontIncludeResourceTransformer
排除掉指定后缀的资源
如下可以排除掉以.txt
结尾的资源
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
<resource>.txt</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
3.0以后的版本,也可以排除一个列表
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
<resources>
<resource>.txt</resource>
<resource>READ.me</resource>
</resources>
</transformer>
IncludeResourceTransformer
和以上转换器相反,可以添加一个资源到包中的项目目录
以下例子,指定了README.txt
添加到 META-INF 目录下
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
<resource>META-INF/README</resource>
<file>README.txt</file>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
ComponentsXmlResourceTransformer
针对Plexus IoC容器的组件的jar包含一个META-INF/plexus /components.xml,该xml文件声明组件及其需求。如果uber JAR聚合了多个 Plexus 组件,则需要使用ComponentsXmlResourceTransformer合并XML描述符:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
PluginXmlResourceTransformer
使用插件工具3.0引入了注释。现在对类的引用不再是字符串形式的类名,而是实际的类引用。当您想要重新定位类时,您必须手工维护META-INF/maven/plugin.xml,但是现在这可以通过PluginXmlResourceTransformer来完成。
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.PluginXmlResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
ServicesResourceTransformer
提供某些接口实现的AR文件通常附带一个META-INF/services/目录,该目录将接口映射到它们的实现类,以便服务定位器进行查找。要重新定位这些实现类的类名,并将同一个接口的多个实现合并到一个服务条目中,可以使用ServicesResourceTransformer:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
ApacheLicenseResourceTransformer
防止License重复。
一些开源的产品(包括 apache 基金会)包含一个他们的License在 META-INF 目录下。
他们一般命名为LICENSE
或者LICENSE.txt
。当合并这个依赖的时候,添加这些资源会照成混乱。ApacheLicenseResourceTransformer 保证复制这些 License 的时候不回合并。
如下的代码可以防止 License 被合并到一个公共集合依赖项中。
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer">
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
ApacheNoticeResourceTransformer
一些许可证(包括Apache许可证,版本2)要求通知由下游分销商保存。ApacheNoticeResourceTransformer自动组装适当的通知。
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
<addHeader>false</addHeader>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
GroovyResourceTransformer
Apache Groovy语言提供了位于META-INF/services/org.codehaus.groovy.runtime.ExtensionModule的扩展模块,这些模块使用属性文件格式。GroovyResourceTransformer自动装配Groovy扩展模块的通知。
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.GroovyResourceTransformer">
<extModuleName>the-aggregated-module</extModuleName>
<extModuleVersion>1.0.0</extModuleVersion>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
------ the end -----