Maven插件

Maven Shade 插件详解

2021-09-12  本文已影响0人  rosy_dawn

简介

该插件提供了将工件打包成 Uber jar 的功能,包括其依赖项,并对某些依赖项的包进行处理。Shade 插件只有一个目标:shade:shade 绑定到 package 阶段,用于创建 Uber jar。问题来了,Uber jar 是什么 jar?请看如下介绍。

Jar 类型

在 Java 中,如果要运行 Java 程序,需要一个包含 main 方法类的 jar 包或类文件,然后执行命令:

# 运行一个含有main方法的.class文件
java $class

# 运行一个jar包,实际上运行的是该jar包中某个类的main方法,这需要在
# 该jar包的清单文件(manifest)中指定一个有main方法的类
java -jar $jarfile

# 使用 -cp (-classpath)  选项用于将jar包添加到类路径中,同时运行jar包
# 里清单文件中所指定的含有main方法的特定类
java -cp (-classpath) $path (directory/jar file/zip file)  # zip 文件应符合jar格式规范

# 如果在jar包里清单文件(manifest)中未指定含有main方法的类,或
# 者该jar包中存在多个含有main方法的类,则可以在上面的命令后面添加指
# 定类的全限定名称(如com.my.Test)来运行该类。
# 这相当于在上面第一条命令中通过 -cp 选项添加了运行的依赖
java -cp (-classpath) $path (directory/jar file/zip file) com.my.Test # zip 文件应符合jar格式规范

关于如何在清单文件中指定默认运行的类以及classpath的内容,请参考 jar 工具详解 中关于 META-INF/MANIFEST.MF 的相关介绍。

这种方法会有一些缺点,因为大多数 Java 程序都包含很多依赖项。如果要启动此程序,必须传递 classpath 以指定这些依赖的包文件,并且必须在服务器上指定类路径,这不是很方便,特别是随着 DevOps/Microservices 的普及,这种指定 classpath 的方法过于死板。我们可以直接构建聚合的 jar 包并发布或运行它。

Executable Jar

可执行 jar 包通常意味着所有依赖的 jar 包都放在一个大的入口 jar 包中。这个入口 jar 包中包含运行时需要依赖的所有 jar 包和类文件。您可以将依赖的 jar 包直接放在入口 jar 包中,如下所示:

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─lib
│  │  ├─io.netty....jar
│  │  ├─com.google....jar
│  │  ├─com.github....jar
│  │  ├─org.apache.....jar

您还可以将依赖的 jar 包中的文件复制到入口 jar 包中,如下所示:

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─io.netty.... classes
│  ├─com.google.. classes
│  ├─com.github.. classes
│  ├─org.apache.. classes

Spring Boot 打包的就是可执行 jar 。Spring Boot 在 spring-boot-maven-plugin 插件可以在构建过程中将所有依赖的 jar 包打包成一个入口 jar 文件,并通过 Spring Boot 的类加载器和启动类来将这些依赖 jar 包加载到可执行的入口 jar 包中,上面描述的第一种方法是:将依赖的 jar 包直接放进入口 jar 包中。

Uber Jar

当我第一次看到这个词时,我不知所措。我不知道这个词是什么意思。优步出租车?查找信息后,我发现 Uber jar 的原始单词是 Über jar,是一个德语单词,可以解释为 over 或 end,但在实际上下文中,将其翻译为 everything 可能更合适。

这个术语最初是由开发人员创造的,他们认为将所有依赖项和自己的代码放入 jar 文件可以解决许多冲突。但大多数输入法很难输入 Ü,所以被称为 Uber。

Fat jar

Fat jar 是对 fat 和 big 的一个很好的解释:Fat jar 和 Uber jar 表示包含所有依赖包的 jar 包。

Shade jar/Shadow jar

Shade jar 就是将依赖包也打包到入口 jar 包的 jar 包,并提供对某些依赖包进行重命名的功能。例如,一个 Maven 项目依赖于许多第三方软件包,但您希望在实际打包期间重命名一些软件包。重命名的过程在这里可以称为 Shade(着色)。

为什么要重命名依赖包呢?例如,当我们开发时还需要依赖一些第三方软件包,比如 netty,所以我们需要在实际操作中以 –javaagent 或动态附加 jar 包的形式加载我们的代理 jar 包。这里加载的代理只能是一个独立的 jar 包,因此首先,我们需要通过在 jar 包中键入我们的代理及其依赖包来构建一个 Uber jar。然后,我们需要考虑类包冲突的问题,因为代理中的依赖包类和目标 JVM 进程中的类可能会发生冲突,例如,代理依赖于 netty 4.1.58.final,而目标 JVM 进程依赖于 netty 4.0.14.final,我们的代理使用 4.0.14 中不存在的 API。此时,程序将产生找不到方法的异常,因为目标进程已加载该类,并且不会重复加载代理程序包中具有相同全限定名的类。

在构建 Uber jar 时,可以修改并重新定位依赖包的包名,这比下载项目的源代码重构包名再打包要方便的多。比如,将 io.netty 修改为 com.github.kongwu.io.netty,同时,Java 代码中的所有引用在重新定位后都使用被修改后的包名。这样,通过修改包名,完全避免了依赖性包类冲突的问题。Google 也开源了一个类似功能的 jar 文件,叫做 jarjar.jar(好多 jar 啊)。

上述 relocation 行为称为 Shade 或 Shadow。Maven 中的 Shade 插件可以将程序打包到单独的jar包中,包括依赖项包。另一个类似的 Maven Assembly 插件也可以达到同样的效果。Gradle 中也有类似的插件,功能也很强大,也支持 Shade 功能。

所有可选参数

示例

选择 Uber Jar 中要包含的内容

下面的 POM 代码片段显示了如何控制 Uber jar 中应包括/排除哪些项目依赖项:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <artifactSet>
                <excludes>
                  <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> 用于指定打包时包含哪些工件。模式中工件由组合标识符表示,其形式为 groupId:artifactId[[:type]:classifier]。从版本 1.3 开始,可以使用通配符 *? 进行类似全局的模式匹配。对于包含选定依赖项中哪些类的细粒度控制,可以使用工件过滤器:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <filters>
                <filter>
                  <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>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

在这里,类似 Ant 的模式用于指定只包含 junit:junit 依赖关系中的某些类/资源到 Uber jar 中。第二个过滤器演示了在 1.3 版本的插件中使用通配符的工件标识。它从每个工件中排除所有与 jar 签名相关的文件,而不管其 groupId 或 artifactId 如何。

除了用户指定的过滤器外,该插件还可以配置为自动删除项目未使用的所有依赖类,从而最大限度地减少产生的 Uber jar 文件的大小:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <minimizeJar>true</minimizeJar>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

从版本 1.6 开始,<minimizeJar> 将尊重那些被特别标记为包含在过滤器中的类。请注意,为工件中的类指定 include 过滤器会隐式地排除该工件中所有未指定的类, <excludeDefaults>false<\excludeDefaults> 将覆盖此行为,因此所有未指定的类仍将包括在内。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <minimizeJar>true</minimizeJar>
              <filters>
                <filter>
                   <artifact>log4j:log4j</artifact>
                   <includes>
                       <include>**</include>
                   </includes>
                </filter>
                <filter>
                   <artifact>commons-logging:commons-logging</artifact>
                   <includes>
                       <include>**</include>
                   </includes>
                </filter>
                <filter>
                   <artifact>foo:bar</artifact>
                   <excludeDefaults>false</excludeDefaults>
                   <includes>
                       <include>foo/Bar.class</include>
                   </includes>
                </filter>
              </filters>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

修改类的包名以避免冲突

如果 Uber jar 被重用为其他项目的依赖项,那么直接将工件依赖项中的类包含在 Uber jar 中可能会由于类路径上的重复类而导致类加载冲突。为了解决这个问题,可以重新定位着色工件中包含的类(即修改 class 文件的包名),以便创建其字节码的私有副本:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>
                    <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>

这指示插件通过移动相应的 jar 文件条目并重写受影响的字节码,将类从包 org.codehaus.plexus.util 及其子包移动到包 org.shade.plexus.util 中。类 Xpp3Dom和其他一些类将保留在其原始包中。还可以使用 <include> 元素来缩小模式范围:

<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>

附加着色工件

默认情况下,插件将用着色工件替换项目的主工件。如果原始工件和着色工件都应安装/部署到仓库,则可以配置该插件以将着色工件作为次要工件附加:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <shadedArtifactAttached>true</shadedArtifactAttached>
              <shadedClassifierName>jackofall</shadedClassifierName> <!-- Any name that makes sense -->
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

着色工件通过额外的 classifier 来与主工件进行区分。

Executable Jar

要创建可执行的 Uber jar,只需设置用作应用程序入口点的主类:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>

这个代码段配置了一个特殊的资源转换器,它在着色 jar 的 MANIFEST.MF 中设置 Main-Class 条目。其他条目也可以通过 <manifestEntries> 部分中的键值对添加到 MANIFEST.MF 文件中:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Main-Class>org.sonatype.haven.ExodusCli</Main-Class>
                    <Build-Number>123</Build-Number>
                  </manifestEntries>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

资源转换

只要不存在重叠,将多个工件中的类/资源聚合到一个 Uber jar 中是很简单的。否则,需要某种逻辑来合并来自多个 jar 的资源。这就是资源转换器的作用所在。

下表是 org.apache.maven.plugins.shade.resource 包中的资源转换器。

资源转换器类名 描述
ApacheLicenseResourceTransformer 防止许可证重复
ApacheNoticeResourceTransformer 准备合并 NOTICE
AppendingTransformer 向一个资源中添加内容
ComponentsXmlResourceTransformer 聚合 Plexus components.xml
DontIncludeResourceTransformer 阻止匹配资源的包含
GroovyResourceTransformer 合并 Apache Groovy 扩展模块
IncludeResourceTransformer 从项目中添加文件
ManifestResourceTransformer 设置 MANIFEST 中的条目
PluginXmlResourceTransformer 合并 Maven plugin.xml
ResourceBundleAppendingTransformer 合并 ResourceBundles
ServicesResourceTransformer 重新修改 META-INF/services 资源中的类名并将它们合并
XmlAppendingTransformer 项 XML 资源文件中添加 XML 内容

下表是 org.apache.maven.plugins.shade.resource.properties 包中的资源转换器(从 3.2.2 版本开始可用)。

资源转换器类名 描述
PropertiesTransformer 合并拥有序号的 properties 文件以解决冲突
OpenWebBeansPropertiesTransformer 合并 Apache OpenWebBeans 配置文件
MicroprofileConfigTransformer 根据序号合并冲突的 Microprofile Config property
使用 ComponentsXmlResourceTransformer 合并 Plexus 组件描述符

作为 Plexus IoC 容器组件的 jar 包含一个 META-INF/Plexus/components.xml 条目,该条目声明组件及其需求。如果 Uber jar 聚合了多个 Plexus 组件,则需要使用 ComponentXmlResourceTransformer 来合并 XML 描述符:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>

从 1.3 版本开始,该资源转换器还将更新描述符,以说明组件接口/实现(如果有)的重新定位。

使用 PluginXmlResourceTransformer 重新定位 Maven 插件描述符的类

随着 Plugin Tools 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.4</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 拼接服务条目

提供某些接口实现的 jar 文件通常附带 META-INF/services/ 目录,该目录将接口映射到它们的实现类,以供服务定位器查找。要重新定位这些实现类的类名,并将同一接口的多个实现合并到一个服务条目中,可以使用 ServicesResourceTransformer:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>
使用 AppendingTransformer、XmlAppendingTransformer 和 ResourceBundleAppendingTransformer 合并特定的文件

某些 jar 包含具有相同文件名的其他资源(如 properties 文件)。为了避免覆盖同名文件,您可以选择通过将其内容附加到一个文件中来合并它们。一个很好的例子是当聚合 spring-context 和 plexus-spring 的 jar 包时,它们都有 META-INF/spring.handlers 文件,Spring 使用该文件处理 XML Schema 命名空间。您可以使用 AppendingTransformer 合并具有该特定名称的所有文件的内容,如下所示:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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.4</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 中。

对于 ResourceBundles properties 文件,您可以改用 ResourceBundleAppendingTransformer,它也将尊重所有可用的本地化设置 :

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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 排除资源

DontIncludeResourceTransformer 允许在资源名称以给定值结尾时排除资源。例如,以下示例排除以 .txt 结尾的所有资源。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>

从 maven-shade-plugin-3.0 开始,还可以提供不应包含的资源列表,如:

<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
  <resources>
    <resource>.txt</resource>
    <resource>READ.me</resource>
  </resources>
</transformer>
使用 IncludeResourceTransformer 添加新资源

IncludeResourceTransformer 允许项目文件以给定名称包含在包中。例如,以下示例在包中包含 README.txt,作为 META-INF 目录中的自述文件。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>
使用 ManifestResourceTransformer 设置清单条目

ManifestResourceTransformer 允许替换 MANIFEST 中的现有条目并添加新条目。例如,

默认情况下,ManifestResourceTransformer 将重新定位以下 attribute:

使用 additionalAttributes,您还可以指定需要重新定位的 attribute。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>
使用 ApacheLicenseResourceTransformer 来防止许可证重复

一些开源生产者(包括 Apache 软件基金会)会在 META-INF 目录中包含其许可证副本,它们通常被命名为 LICENSE 或 LICENSE.txt。合并这些依赖项时,添加这些资源可能会导致许可证文件冲突。ApacheChenseResourceTransformer 确保不会合并重复的许可证(根据该约定命名)。例如,以下内容可防止将 commons-collections 依赖项的许可证出现合并冲突。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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 来聚合 Notice

一些许可证(包括 Apache License, Version 2)要求下游发布者必需保留该许可对外的一些 NOTICEApacheNoticeResourceTransformer 自动组装适当的 NOTICE。例如,要简单地合并到依赖的告知中请使用如下配置:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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 扩展模块描述符

Apache Groovy 语言在 META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 目录提供了扩展模块,这些模块使用 property 文件格式。GroovyResourceTransformer 自动组装 Groovy 扩展模块的 NOTICE。例如,简单地合并几个 jar 的扩展模块如下:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</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>
使用 PropertiesTransformer 来合并 properties 文件

PropertiesTransformer 允许合并一组 properties 文件,并根据赋予每个文件优先级的序号解决冲突。可选的 alreadyMergedKey 允许在文件中使用布尔标志,如果该标志设置为 true,则请求将文件作为合并的结果使用。如果两个文件在合并过程中被认为是完整的,则着色将失败。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer">
                  <!-- required configuration -->
                  <resource>configuration/application.properties</resource>
                  <ordinalKey>ordinal</ordinalKey>
                  <!-- optional configuration -->
                  <alreadyMergedKey>already_merged</alreadyMergedKey>
                  <defaultOrdinal>0</defaultOrdinal>
                  <reverseOrder>false</reverseOrder>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
使用 OpenWebBeansPropertiesTransformer 来合并 Apache OpenWebBeans 配置

OpenWebBeansPropertiesTransformer 为 Apache OpenWebBeans 配置文件预配置一个 PropertiesTransformer

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.properties.OpenWebBeansPropertiesTransformer" />
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
使用 MicroprofileConfigTransformer 来合并 Microprofile Config properties

MicropFileConfigTransformer 为 Microprofile Config 预配置一个 PropertiesTransformer。唯一需要的配置是序号。支持 alreadyMergedKey,但规范中未定义。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.properties.MicroprofileConfigTransformer">
                  <resource>configuration/app.properties</resource>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

使用自定义的着色器实现

默认情况下,该插件提供 DefaultShader 实现,但在版本1.6中,您可以使用自己的实现。

下面创建一个标准 Maven 项目来使用您自己的实现。

Dependency to Plexus annotations
 
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-component-annotations</artifactId>
      <version>1.5.5</version>
    </dependency>
 
Create your Shader
 
@Component( role = Shader.class, hint = "mock" )
public class MockShader implements Shader {
  // implement the interface here
}
 
// Use the plexus component metadata plugin in your job to generate Plexus metadata
 
      <plugin>
        <groupId>org.codehaus.plexus</groupId>
        <artifactId>plexus-component-metadata</artifactId>
        <version>1.5.5</version>
        <executions>
          <execution>
            <goals>
              <goal>generate-metadata</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

假设您的项目坐标为 org.foo.bar:wine:1.0,则必须将其添加为着色插件的依赖项。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <dependencies>
          <dependency>
            <groupId>org.foo.bar</groupId>
            <artifactId>wine</artifactId>
            <version>1.0</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <shaderHint>mock</shaderHint>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

现在,mojo 将使用您自己的实现。

上一篇下一篇

猜你喜欢

热点阅读