maven-shade-plugin的用途是什么?为什么要重定位Java包?


281

我发现有人的pom.xml中使用了maven-shade-plugin。我以前从未使用过maven-shade-plugin(并且我是Maven n00b),所以我试图了解使用它的原因及其作用。

我看了看Maven文档,但是我听不懂这句话:

“该插件提供了将工件打包在uber-jar中的功能,包括其依赖项,并可以遮蔽(即重命名)某些依赖项的包。”

该页面上的文档似乎不太适合新手。

什么是“超级罐子”?为什么有人要做一个?重命名依赖包的意义何在?我尝试浏览了Maven-shade-plugin apache页面上的示例,例如“为Uber Jar选择内容”,但是我仍然不明白使用“ shading”可以完成什么。

任何指向说明性示例/用例的指示(带有在这种情况下为什么需要阴影的解释-它要解决什么问题)将不胜感激。最后,什么时候应该使用maven-shade-plugin?


16
对于命名“超级罐子”,我与德语具有唯一的关联,其中“über”表示“结束”。在一起,它的字面意思是“罐在所有其他罐子上”。“阴影”与冲突的类或资源所需的“包重新分配”相同。
dma_k 2013年

1
s/reallocation/relocation/在上面的评论中。
Christopher Schultz'1

12
超级罐子就像一枚戒指:一个罐子统治着所有人,一个罐子寻找他们,一个罐子将所有东西带走,在黑暗中将它们捆绑在一起。
Marti Nito

Answers:


342

简而言之,Uber JAR是包含所有内容的JAR。

通常在Maven中,我们依赖于依赖项管理。工件仅包含其自身的类/资源。Maven将负责根据项目的构建时间来找出项目的所有工件(JAR等)。

uber-jar是一种具有所有依赖关系的东西,可以提取依赖关系的内容,并将其与项目本身的类/资源放在一个大的JAR中。通过拥有这样的uber-jar,它很容易执行,因为您只需要一个大JAR即可运行大量应用,而无需大量的小JAR。在某些情况下,这也简化了分配。

只是一个旁注。避免将uber-jar用作Maven依赖项,因为它破坏了Maven的依赖项解析功能。通常,我们仅为最终工件创建uber-jar以便进行实际部署或手动分发,而不是将其放入Maven存储库。


更新:我刚刚发现我还没有回答问题的一部分:“重命名依赖包有什么意义?”。这是一些简短的更新,希望对有类似问题的人们有所帮助。

创建uber-jar以便于部署是shade插件的一个用例。还有其他一些常见的用例,涉及包重命名。

例如,我正在开发Foo库,这取决于库的特定版本(例如1.0)Bar。假设我无法使用其他版本的Barlib(因为API更改或其他技术问题等)。如果我只是在Maven中声明Bar:1.0Foo的依赖项,则可能会遇到一个问题:一个Qux项目依赖于Foo,并且也依赖于Bar:2.0(并且它不能使用,Bar:1.0因为Qux需要使用中的新功能Bar:2.0)。这是一个难题:应该Qux使用Bar:1.0(哪个Qux代码不起作用)还是Bar:2.0(哪个Foo代码不起作用)?

为了解决此问题,of的开发人员Foo可以选择使用shade插件来重命名其用法Bar,以便将Bar:1.0jar中的所有类都嵌入Foojar中,并将嵌入Bar类的包从更改com.barcom.foo.bar。这样,Qux可以安全地依赖,Bar:2.0因为现在Foo不再依赖Bar,并且它使用的是Bar位于另一个包中的“更改”的自己的副本。


5
谢谢,这很有帮助。因为它隐藏了uber-jar中的依赖项和其他jar,所以它称为“阴影”吗?
2012年

6
我不确定名称背后的原因,但从其首页看,似乎表明“阴影”描述了依赖项的“隐藏”。由于您可以选择在阴影JAR中包含一些依赖项,因此shade插件还将为您生成一个正确的POM,以删除包含的依赖项。似乎阴影描述了此过程
Adrian Shum 2012年

1
@AdrianShum:您能建议不使用shade插件如何创建uber jar吗?还是专门解决“ Maven依赖关系解析功能的破坏”?
Suraj Menon 2014年

3
@SurajMenon:使用Shade插件是创建uber-jar的最简单方法。但是,您也可以使用Assembly插件,并使用内置jar-with-dependency描述符。对于使用uber-jar的依赖关系腐蚀问题,我已经在回答中提到:不要将uber-jar用作依赖关系,句点。详细一点:在创建uber-jar之前,您应该有一个具有正常依赖项的普通项目。那原始的工件是您应该用作依赖的工件(而不是uber-jar)
Adrian Shum 2014年

1
只是一个小问题:它会停止Class.forName工作吗?
SOFe

64

我最近在想自己为什么elasticsearch会遮蔽并重新定位一些(但不是全部)依赖项。这是项目维护者@kimchy的解释:

阴影部分是有意的,我们在Elasticsearch中使用的阴影库是针对Elasticsearch的所有意图和目的部分,所使用的版本与Elasticsearch公开的内容以及基于库工作方式的内部使用方式紧密相关(以及版本之间的差异),netty和番石榴就是很好的例子。

顺便说一句,我实际上可以提供几个jar的elasticsearch,一个不带Lucene阴影,另一个带Lucene阴影。虽然不确定如何使用Maven进行操作。我不想提供一个不会掩盖netty / jackson的版本,例如,elasticsearch对它们有很深的使用习惯(例如,对netty的任何先前版本使用即将推出的缓冲改进功能,但当前版本除外)实际使用的内存要多于使用较少的内存)。

- https://github.com/elasticsearch/elasticsearch/issues/2091#issuecomment-7156766

还有一个来自drewr的信息

阴影对于使我们的依赖关系(尤其是netty,lucene,番石榴)与我们的代码保持接近非常重要,这样即使上游提供程序落后,我们也可以解决问题。我们可能会分发代码的模块化版本,这将有助于解决您的特定问题(例如,#2091),但是目前我们不能简单地删除阴影的依赖项。您可以构建自己的ES本地版本,直到找到更好的解决方案为止。

- https://github.com/elasticsearch/elasticsearch/pull/3244#issuecomment-20125452

因此,这是一个用例。作为一个说明性示例,以下是在Elasticsearch的pom.xml(v0.90.5)中如何使用maven-shade-plugin。这些artifactSet::include行指示它要放入uber JAR中的依赖项(基本上,它们会解压缩并在生成目标Elasticsearch jar时与Elasticsearch自己的类一起重新打包。(如果您还不知道,则JAR文件是只是一个ZIP文件,其中包含程序的类,资源等,以及一些元数据。您可以提取其中一个文件来查看其组合方式。)

这些relocations::relocation行是相似的,除了在每种情况下它们还将指定的替换应用于依赖项的类-在这种情况下,将它们置于之下org.elasticsearch.common

最后,本filters节从目标JAR中排除了一些不应包含在其中的东西-例如JAR元数据,蚂蚁构建文件,文本文件等,它们打包在一起并具有某些依赖项,但不属于超级JAR。

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <minimizeJar>true</minimizeJar>
            <artifactSet>
                <includes>
                    <include>com.google.guava:guava</include>
                    <include>net.sf.trove4j:trove4j</include>
                    <include>org.mvel:mvel2</include>
                    <include>com.fasterxml.jackson.core:jackson-core</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-smile</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-yaml</include>
                    <include>joda-time:joda-time</include>
                    <include>io.netty:netty</include>
                    <include>com.ning:compress-lzf</include>
                </includes>
            </artifactSet>
            <relocations>
                <relocation>
                    <pattern>com.google.common</pattern>
                    <shadedPattern>org.elasticsearch.common</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>gnu.trove</pattern>
                    <shadedPattern>org.elasticsearch.common.trove</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166y</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166y</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166e</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166e</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.mvel2</pattern>
                    <shadedPattern>org.elasticsearch.common.mvel2</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.fasterxml.jackson</pattern>
                    <shadedPattern>org.elasticsearch.common.jackson</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.joda</pattern>
                    <shadedPattern>org.elasticsearch.common.joda</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.jboss.netty</pattern>
                    <shadedPattern>org.elasticsearch.common.netty</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.ning.compress</pattern>
                    <shadedPattern>org.elasticsearch.common.compress</shadedPattern>
                </relocation>
            </relocations>
            <filters>
                <filter>
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/license/**</exclude>
                        <exclude>META-INF/*</exclude>
                        <exclude>META-INF/maven/**</exclude>
                        <exclude>LICENSE</exclude>
                        <exclude>NOTICE</exclude>
                        <exclude>/*.txt</exclude>
                        <exclude>build.properties</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
    </plugin>
</plugins>

2

小警告

尽管这还不能说明为什么要使用Maven-shade-plugin(因为所选答案描述得很好),但我想指出我对此有问题。它更改了JAR(因为它在做什么),并且导致了我的软件的回归。

因此,我没有使用此(或maven-jarjar-plugin),而是使用了JarJar的二进制文件,该二进制文件似乎可以正常工作。

我在这里发布我的解决方案,因为花了一些时间才找到一个不错的解决方案。


Downlaod JarJar的JAR文件

您可以从以下位置下载jar:https//code.google.com/p/jarjar/ 在左侧菜单中,您有一个下载链接。


如何使用JarJar以便将JAR的类从一个包重定位到另一个包

在此示例中,我们将包从“ com.fasterxml.jackson”更改为“ io.kuku.dependencies.com.fasterxml.jackson”。-源JAR被称为“ jackson-databind-2.6.4.jar”,新的修改的(目标)JAR被称为“ kuku-jackson-databind-2.6.4.jar”。-“ jarjar” JAR文件的版本为1.4

  1. 创建一个“ rules.txt”文件。文件内容应为(注意“ @”字符前的句点):规则com.fasterxml.jackson。** io.kuku.dependencies.com.fasterxml.jackson。@ 1

  2. 运行以下命令:java -jar jarjar-1.4.jar处理rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


将修改后的JAR安装到本地存储库

在这种情况下,我将安装位于“ c:\ my-jars \”文件夹中的3个文件。

mvn install:安装文件-Dfile = C:\ my-jars \ kuku-jackson-annotations-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4- Dpackaging = jar

mvn install:安装文件-Dfile = C:\ my-jars \ kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-core -Dversion = 2.6.4- Dpackaging = jar

mvn install:安装文件-Dfile = C:\ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4- Dpackaging = jar


在项目的pom中使用修改后的JAR

在此示例中,这是项目pom中的“ dependencies”元素:

<dependencies>
    <!-- ================================================== -->
    <!-- kuku JARs -->
    <!-- ================================================== -->
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-annotations</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>
</dependencies>

1
谢谢您的替代建议。这不是我想要的,但事实证明,这是一种将1次软件包翻译应用于永远不会改变的遗留库的更简单快捷的解决方案。
弗林

您是否通过比较各自输出的内容来找出此类故障的原因?
tribbloid

2

我认为需要“阴影” jar的一个示例是AWS Lambda函数。他们似乎只允许您上载1个jar,而不像典型的.war文件中那样找到整个.jars集合。因此,通过创建一个具有所有项目依赖项的.jar,您可以执行此操作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.