maven-shade-plugin 使用方法

本文翻译源于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>

定义自己的shade实现

------ the end -----

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容