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

我发现有人在pom.xml中使用了maven-shade-plugin。我以前从未使用过Maven -shade-plugin(我是一个Maven n00b),所以我试图了解使用这个插件的原因和它的功能。

我看了Maven文档,但是我不能理解这个语句:

这个插件提供了将工件打包到一个super -jar中的能力,包括它的依赖项,以及对一些依赖项的包进行着色(即重命名)。

页面上的文档似乎对新手不太友好。

什么是“超级罐子”?为什么会有人想做一个呢?重命名依赖包的意义何在?我试着浏览了maven-shade-plugin apache页面上的例子,比如“为Uber Jar选择内容”;但是我还是不明白“着色”是怎么完成的。

任何指向说明性示例/用例的指针(解释为什么在这种情况下需要阴影-它解决了什么问题)都将是非常感谢的。最后,我应该什么时候使用maven-shade-plugin?

125970 次浏览

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

通常在Maven中,我们依赖依赖项管理。工件只包含自身的类/资源。Maven将负责在项目构建时找出项目所依赖的所有工件(jar等)。

super - JAR可以获取所有依赖项,并提取依赖项的内容,并将它们与项目本身的类/资源放在一个大JAR中。有了这样一个超级JAR,执行起来就很容易了,因为你只需要一个大JAR就可以运行你的应用,而不是无数个小JAR。在某些情况下,这也简化了应用的分发。

只是一个旁注:避免使用super -jar作为Maven依赖项,因为它破坏了Maven的依赖项解析特性。通常,我们只为实际部署或手动分发的最终工件创建一个super -jar,而不是为放入Maven存储库而创建。


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

创建一个超级jar以方便部署是shade插件的一个用例。还有其他涉及包重命名的常见用例。

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

为了解决这个问题,Foo的开发人员可以选择使用shade插件来重命名Bar的用法,这样Bar:1.0 jar中的所有类都被嵌入到Foo jar中,并且嵌入的Bar类的包从com.bar改为com.foo.bar。通过这样做,Qux可以安全地依赖于Bar:2.0,因为现在Foo不再依赖于Bar,并且它正在使用自己的&;Bar位于另一个包中。

enter image description here

最近我一直在想,为什么elasticsearch会隐藏和重定位一些(但不是全部)依赖项。下面是项目维护者< a href = " https://stackoverflow.com/users/275993/kimchy " > @kimchy < / >的解释:

阴影部分是有意的,我们在elasticsearch中使用的阴影库是为了elasticsearch的所有意图和目的部分,所使用的版本与elasticsearch所公开的版本紧密联系在一起,以及它如何基于库的内部工作方式(以及在版本之间的变化)使用库,netty和guava就是很好的例子。

顺便说一句,我没有问题,实际上提供了几罐elasticsearch,一个lucene没有阴影,和一个lucene阴影。但不确定如何使用maven。例如,我不想提供一个不覆盖netty/jackson的版本,因为elasticsearch已经与他们有了很深的亲密关系(例如,使用即将到来的缓冲改进与任何以前版本的netty(除了当前版本)实际上会使用更多的内存,而不是使用相当少的内存)。

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

另一个来自drewr:

阴影对于保持我们的依赖关系(特别是netty, lucene, guava)接近我们的代码很重要,这样即使上游提供者滞后,我们也可以修复问题。我们可能会分布式模块化版本的代码,这将有助于您的特定问题(例如#2091),但我们不能简单地删除阴影依赖项。在出现更好的解决方案之前,您可以根据自己的需要构建ES的本地版本。

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

这是一个用例。作为一个说明性的例子,下面是如何在elasticsearch的pom.xml (v0.90.5)中使用maven-shade-plugin。artifactSet::include行指示它将哪些依赖项拉入优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>

小的警告

虽然这并没有描述为什么人们会喜欢使用maven-shade-plugin(因为所选的答案描述得很好),但我想指出的是,我对它有问题。它改变了JAR(因为它正在做这件事),并在我的软件中造成了倒退。

所以,我没有使用这个(或maven-jarjar-plugin),而是使用了JarJar的二进制文件,它似乎没有任何问题。

我在这里张贴我的解决方案,因为我花了一些时间来找到一个像样的解决方案。


下载JarJar的JAR文件

你可以从这里下载jar: https://code.google.com/p/jarjar/ 在左边的菜单中你有一个下载的链接


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

在这个例子中,我们将把包从"com.fasterxml. xml "改为"杰克逊”到“杰克逊”。 源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 < / p > < /李>

  2. 执行以下命令: Java -jar jarjar-1.4.jar进程规则。txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar

  3. .jar

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

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

mvn install:install-file -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:install-file -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:install-file -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>

我认为需要“阴影”jar的一个例子是AWS Lambda函数。他们似乎只允许你上传1个jar,而不是像你在典型的.war文件中发现的整个。jar集合。因此,创建一个包含项目所有依赖项的.jar就可以做到这一点。