Maven Shade 插件詳解

簡介

該插件提供了將工件打包成 Uber jar 的功能,包括其依賴項,并對某些依賴項的包進行處理。Shade 插件只有一個目標:shade:shade 綁定到 package 階段,用于創(chuàng)建 Uber jar。問題來了,Uber jar 是什么 jar?請看如下介紹。

Jar 類型

在 Java 中,如果要運行 Java 程序,需要一個包含 main 方法類的 jar 包或類文件,然后執(zhí)行命令:

# 運行一個含有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格式規(guī)范

# 如果在jar包里清單文件(manifest)中未指定含有main方法的類,或
# 者該jar包中存在多個含有main方法的類,則可以在上面的命令后面添加指
# 定類的全限定名稱(如com.my.Test)來運行該類。
# 這相當于在上面第一條命令中通過 -cp 選項添加了運行的依賴
java -cp (-classpath) $path (directory/jar file/zip file) com.my.Test # zip 文件應符合jar格式規(guī)范

關(guān)于如何在清單文件中指定默認運行的類以及classpath的內(nèi)容,請參考 jar 工具詳解 中關(guān)于 META-INF/MANIFEST.MF 的相關(guān)介紹。

這種方法會有一些缺點,因為大多數(shù) Java 程序都包含很多依賴項。如果要啟動此程序,必須傳遞 classpath 以指定這些依賴的包文件,并且必須在服務器上指定類路徑,這不是很方便,特別是隨著 DevOps/Microservices 的普及,這種指定 classpath 的方法過于死板。我們可以直接構(gòu)建聚合的 jar 包并發(fā)布或運行它。

Executable Jar

可執(zhí)行 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 打包的就是可執(zhí)行 jar 。Spring Boot 在 spring-boot-maven-plugin 插件可以在構(gòu)建過程中將所有依賴的 jar 包打包成一個入口 jar 文件,并通過 Spring Boot 的類加載器和啟動類來將這些依賴 jar 包加載到可執(zhí)行的入口 jar 包中,上面描述的第一種方法是:將依賴的 jar 包直接放進入口 jar 包中。

Uber Jar

當我第一次看到這個詞時,我不知所措。我不知道這個詞是什么意思。優(yōu)步出租車?查找信息后,我發(fā)現(xiàn) Uber jar 的原始單詞是 über jar,是一個德語單詞,可以解釋為 over 或 end,但在實際上下文中,將其翻譯為 everything 可能更合適。

這個術(shù)語最初是由開發(fā)人員創(chuàng)造的,他們認為將所有依賴項和自己的代碼放入 jar 文件可以解決許多沖突。但大多數(shù)輸入法很難輸入 ü,所以被稱為 Uber。

Fat jar

Fat jar 是對 fat 和 big 的一個很好的解釋:Fat jar 和 Uber jar 表示包含所有依賴包的 jar 包。

Shade jar/Shadow jar

Shade jar 就是將依賴包也打包到入口 jar 包的 jar 包,并提供對某些依賴包進行重命名的功能。例如,一個 Maven 項目依賴于許多第三方軟件包,但您希望在實際打包期間重命名一些軟件包。重命名的過程在這里可以稱為 Shade(著色)。

為什么要重命名依賴包呢?例如,當我們開發(fā)時還需要依賴一些第三方軟件包,比如 netty,所以我們需要在實際操作中以 –javaagent 或動態(tài)附加 jar 包的形式加載我們的代理 jar 包。這里加載的代理只能是一個獨立的 jar 包,因此首先,我們需要通過在 jar 包中鍵入我們的代理及其依賴包來構(gòu)建一個 Uber jar。然后,我們需要考慮類包沖突的問題,因為代理中的依賴包類和目標 JVM 進程中的類可能會發(fā)生沖突,例如,代理依賴于 netty 4.1.58.final,而目標 JVM 進程依賴于 netty 4.0.14.final,我們的代理使用 4.0.14 中不存在的 API。此時,程序?qū)a(chǎn)生找不到方法的異常,因為目標進程已加載該類,并且不會重復加載代理程序包中具有相同全限定名的類。

在構(gòu)建 Uber jar 時,可以修改并重新定位依賴包的包名,這比下載項目的源代碼重構(gòu)包名再打包要方便的多。比如,將 io.netty 修改為 com.github.kongwu.io.netty,同時,Java 代碼中的所有引用在重新定位后都使用被修改后的包名。這樣,通過修改包名,完全避免了依賴性包類沖突的問題。Google 也開源了一個類似功能的 jar 文件,叫做 jarjar.jar(好多 jar 啊)。

上述 relocation 行為稱為 Shade 或 Shadow。Maven 中的 Shade 插件可以將程序打包到單獨的jar包中,包括依賴項包。另一個類似的 Maven Assembly 插件也可以達到同樣的效果。Gradle 中也有類似的插件,功能也很強大,也支持 Shade 功能。

所有可選參數(shù)

  • <artifactSet>。聲明要從最終工件中包括/排除的工件。工件由組合標識符表示,其一般形式為 groupId:artifactId:type:classifier。從 1.3 版開始,通配符 *? 可以在這些復合標識符的子部分中使用,以進行模式匹配。為方便起見,語法 groupId 等效于 groupId:*:*:*groupId:artifactId 等效于 groupId:artifactId:*:*groupId:artifactId:classifier 等效于 groupId:artifactId:*:classifier。例如:

    <artifactSet>
      <includes>
        <include>org.apache.maven:*</include>
      </includes>
      <excludes>
        <exclude>*:maven-core</exclude>
      </excludes>
    </artifactSet>
    
  • <createDependencyReducedPom>。表示是否為 Shaded 工件生成簡化的 POM。如果設(shè)置為 true(默認),則已包含在 Uber jar 中的依賴項將從生成的POM 的 <dependencies> 部分中刪除。簡化后的 POM 將命名為 dependency-reduced-pom.xml,并存儲在與 Shaded 工件相同的目錄中。除非還指定dependencyReducedPomLocation,否則插件將在項目 basedir 中創(chuàng)建一個名為 dependency-reduced-pom.xml 的臨時文件。

  • <createSourcesJar>。如果為 true,它也將嘗試創(chuàng)建一個 sources jar。

  • <createTestSourcesJar>。如果為 true,它將嘗試創(chuàng)建一個 test sources jar。

  • <dependencyReducedPomLocation>。在何處生成 dependency-reduced-pom.xml 文件。默認為 ${basedir}/dependency-reduced-pom.xml。注意:使用 ${basedir} 以外的目錄來作為該參數(shù)的值將更改后續(xù) Shade 操作中所涉及的 ${basedir} 的值。這通常不是你想要的。這被認為是這個插件的一個公開問題。

  • <filters>。要使用的歸檔篩選器。允許以復合標識符的形式指定一個工件,該標識符由 artifactSet 和一組 include/exclude 文件模式使用,用于過濾將哪些內(nèi)容添加到 Shade jar 中。從邏輯角度看,include 在 exclude 之前進行處理,因此可以使用 include 從歸檔中收集一組文件,然后使用 exclude 進一步減少該組文件。默認情況下,包含所有文件,不排除任何文件。如果對一個工件應用多個過濾器,那么匹配文件的交集將包含在最終的 jar 中。例如:

    <filters>
      <filter>
        <artifact>junit:junit</artifact>
        <includes>
          <include>org/junit/**</include>
        </includes>
        <excludes>
          <exclude>org/junit/experimental/**</exclude>
        </excludes>
      </filter>
    </filters>
    
  • <finalName>。要 Shade 的 artifactId 的名稱。如果您想更改該工件的名稱,可以使用 <build><finalName> 設(shè)置。如果將其設(shè)置為與 <build><finalName> 不同的內(nèi)容,則不會執(zhí)行文件替換,即使正在使用 shadedArtifactAttached

  • <generateUniqueDependencyReducedPom>。在 ${basedir}/drp-UNIQUE.pom 中創(chuàng)建一個 dependency-reduced 的 POM,默認為 false。這避免了并行構(gòu)建的構(gòu)建沖突,而無需將 dependency-reduced 的 POM 移動到其他目錄。名為 maven.shade.dependency-reduced-pom property 用于設(shè)置為生成的文件名。

  • <keepDependenciesWithProvidedScope>。如果為 true,則依賴項保留在 pom 中,但其 scopeprovided;如果為 false,則刪除依賴項。

  • <minimizeJar>。如果為 true,則依賴關(guān)系將在類級別上剝離,僅為工件所需的可傳遞外殼。注意:使用此功能需要 Java 1.5 或更高版本。

  • <outputDirectory>。Shaded 工件的輸出目錄。

  • <outputFile>。Shaded 工件的輸出文件路徑。設(shè)置此參數(shù)后,創(chuàng)建的歸檔文件既不會替換項目的主工件,也不會附加它。因此,此參數(shù)會導致使用時忽略參數(shù) finalNameshadedArtifactAttachedshadedClassifierNamecreateDependencyReducedPom

  • <promoteTransitiveDependencies>。如果為 true,則已刪除的依賴項的可傳遞依賴將升級為直接依賴項。這應該允許使用新的 Shade jar 來替換已移除的依賴,并且一切仍然可以工作。

  • <relocations>。要重新定位的包。例如:

    <relocations>
      <relocation>
        <pattern>org.apache</pattern>
        <shadedPattern>hidden.org.apache</shadedPattern>
        <includes>
          <include>org.apache.maven.*</include>
        </includes>
        <excludes>
          <exclude>org.apache.maven.Public*</exclude>
        </excludes>
      </relocation>
    </relocations>
    

    僅從版本 1.4 起才存在對 include 的支持。

  • <shadeSourcesContent>。如果為 true,它將在創(chuàng)建 sources jar 時嘗試著色 java 源文件的內(nèi)容。如果為 false,它只會將 java 源文件重新定位到著色路徑,但不會修改 java 源文件的實際內(nèi)容。默認為 false

  • <shadeTestJar>。如果為 true,則也會創(chuàng)建一個著色的 test jar 工件。默認為 false

  • <shadedArtifactAttached>。定義著色工件是否應作為 classifier 附加到原始工件。如果為 false,著色的 jar 將是項目的主要工件。

  • <shadedArtifactId>。著色工件的名稱,默認為 ${project.artifactId}。當想使用一個不同的 artifaceId 并保留原始的 artifaceId 值時可以使用該參數(shù)。如果原始的 artifactIdfoo,那么最終的工件將類似于 foo-1.0.jar。因此,如果您更改了該參數(shù)值,您可能創(chuàng)建類似 foo-special-1.0.jar 名稱的文件。

  • <shadedClassifierName>。附加著色工件時使用的 classifier 的名稱。默認為 shaded

  • <shadedGroupFilter>。如果指定,這將只包括具有以此開頭的 groupId 的工件。

  • <shaderHint>。您可以在這里傳遞有關(guān)您自己的著色器實現(xiàn) plexus 組件的 roleHint

  • <transformers>。要使用的資源轉(zhuǎn)換器。

  • <useBaseVersion>。如果為 true,則簡化 pom 中的每個依賴項的版本將基于原始依賴項的 baseVersion,而不是其解析的版本。例如,如果原始 pom(可傳遞)依賴于a:a:2.7-SNAPSHOT,如果 useBaseVersion 設(shè)置為 false(默認),則簡化 pom 將依賴于 a:a:2.7-20130312.222222-12,而如果useBaseVersion 設(shè)置為 true,則簡化 pom 將依賴于 a:a:2.7-SNAPSHOT

示例

選擇 Uber Jar 中要包含的內(nèi)容

下面的 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 依賴關(guān)系中的某些類/資源到 Uber jar 中。第二個過濾器演示了在 1.3 版本的插件中使用通配符的工件標識。它從每個工件中排除所有與 jar 簽名相關(guān)的文件,而不管其 groupId 或 artifactId 如何。

除了用戶指定的過濾器外,該插件還可以配置為自動刪除項目未使用的所有依賴類,從而最大限度地減少產(chǎn)生的 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> 將覆蓋此行為,因此所有未指定的類仍將包括在內(nèi)。

<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 文件的包名),以便創(chuàng)建其字節(jié)碼的私有副本:

<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 文件條目并重寫受影響的字節(jié)碼,將類從包 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 來與主工件進行區(qū)分。

Executable Jar

要創(chuàng)建可執(zhí)行的 Uber jar,只需設(shè)置用作應用程序入口點的主類:

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

這個代碼段配置了一個特殊的資源轉(zhuǎn)換器,它在著色 jar 的 MANIFEST.MF 中設(shè)置 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>

資源轉(zhuǎn)換

只要不存在重疊,將多個工件中的類/資源聚合到一個 Uber jar 中是很簡單的。否則,需要某種邏輯來合并來自多個 jar 的資源。這就是資源轉(zhuǎn)換器的作用所在。

下表是 org.apache.maven.plugins.shade.resource 包中的資源轉(zhuǎn)換器。

資源轉(zhuǎn)換器類名 描述
ApacheLicenseResourceTransformer 防止許可證重復
ApacheNoticeResourceTransformer 準備合并 NOTICE
AppendingTransformer 向一個資源中添加內(nèi)容
ComponentsXmlResourceTransformer 聚合 Plexus components.xml
DontIncludeResourceTransformer 阻止匹配資源的包含
GroovyResourceTransformer 合并 Apache Groovy 擴展模塊
IncludeResourceTransformer 從項目中添加文件
ManifestResourceTransformer 設(shè)置 MANIFEST 中的條目
PluginXmlResourceTransformer 合并 Maven plugin.xml
ResourceBundleAppendingTransformer 合并 ResourceBundles
ServicesResourceTransformer 重新修改 META-INF/services 資源中的類名并將它們合并
XmlAppendingTransformer 項 XML 資源文件中添加 XML 內(nèi)容

下表是 org.apache.maven.plugins.shade.resource.properties 包中的資源轉(zhuǎn)換器(從 3.2.2 版本開始可用)。

資源轉(zhuǎn)換器類名 描述
PropertiesTransformer 合并擁有序號的 properties 文件以解決沖突
OpenWebBeansPropertiesTransformer 合并 Apache OpenWebBeans 配置文件
MicroprofileConfigTransformer 根據(jù)序號合并沖突的 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 版本開始,該資源轉(zhuǎn)換器還將更新描述符,以說明組件接口/實現(xiàn)(如果有)的重新定位。

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

隨著 Plugin Tools 3.0 的引入。現(xiàn)在對類的引用不再是作為字符串的類名,而是實際的類引用。當您想要重新定位類時,您必須手動維護 META-INF/maven/plugin.xml,但現(xiàn)在可以使用 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 拼接服務條目

提供某些接口實現(xiàn)的 jar 文件通常附帶 META-INF/services/ 目錄,該目錄將接口映射到它們的實現(xiàn)類,以供服務定位器查找。要重新定位這些實現(xiàn)類的類名,并將同一接口的多個實現(xiàn)合并到一個服務條目中,可以使用 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 文件)。為了避免覆蓋同名文件,您可以選擇通過將其內(nèi)容附加到一個文件中來合并它們。一個很好的例子是當聚合 spring-context 和 plexus-spring 的 jar 包時,它們都有 META-INF/spring.handlers 文件,Spring 使用該文件處理 XML Schema 命名空間。您可以使用 AppendingTransformer 合并具有該特定名稱的所有文件的內(nèi)容,如下所示:

<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,從而避免網(wǎng)絡(luò)訪問。這種模式的潛在缺點是無法解析可能導致轉(zhuǎn)換失敗的外部實體,例如,當使用某些 JRE 1.4 中使用的 Crimson XML 解析器時。如果轉(zhuǎn)換后的資源使用外部實體,則可以重新啟用 DTD 解析,或者將 xerces:xercesImpl:2.9.1 的插件依賴項添加到 POM 中。

對于 ResourceBundles properties 文件,您可以改用 ResourceBundleAppendingTransformer,它也將尊重所有可用的本地化設(shè)置 :

<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 允許在資源名稱以給定值結(jié)尾時排除資源。例如,以下示例排除以 .txt 結(jié)尾的所有資源。

<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 設(shè)置清單條目

ManifestResourceTransformer 允許替換 MANIFEST 中的現(xiàn)有條目并添加新條目。例如,

  • app.main.class property 的值的 Main-Class 條目;
  • maven.compile.source property 的值是 X-Compile-Source-JDK 條目;
  • maven.compile.target property 的值是 X-Compile-Target-JDK 條目。

默認情況下,ManifestResourceTransformer 將重新定位以下 attribute:

  • Export-Package
  • Import-Package
  • Provide-Capability
  • Require-Capability

使用 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 來防止許可證重復

一些開源生產(chǎn)者(包括 Apache 軟件基金會)會在 META-INF 目錄中包含其許可證副本,它們通常被命名為 LICENSE 或 LICENSE.txt。合并這些依賴項時,添加這些資源可能會導致許可證文件沖突。ApacheChenseResourceTransformer 確保不會合并重復的許可證(根據(jù)該約定命名)。例如,以下內(nèi)容可防止將 commons-collections 依賴項的許可證出現(xiàn)合并沖突。

<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)要求下游發(fā)布者必需保留該許可對外的一些 NOTICEApacheNoticeResourceTransformer 自動組裝適當?shù)?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 文件,并根據(jù)賦予每個文件優(yōu)先級的序號解決沖突。可選的 alreadyMergedKey 允許在文件中使用布爾標志,如果該標志設(shè)置為 true,則請求將文件作為合并的結(jié)果使用。如果兩個文件在合并過程中被認為是完整的,則著色將失敗。

<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,但規(guī)范中未定義。

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

使用自定義的著色器實現(xiàn)

默認情況下,該插件提供 DefaultShader 實現(xiàn),但在版本1.6中,您可以使用自己的實現(xiàn)。

下面創(chuàng)建一個標準 Maven 項目來使用您自己的實現(xiàn)。

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>

假設(shè)您的項目坐標為 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>

現(xiàn)在,mojo 將使用您自己的實現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容