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

我看了Maven文档,但我不明白这句话:

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

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

什么是“超级罐子”?为什么会有人想做一个呢?重命名依赖包的意义何在?我试图通过maven-shade-plugin apache页面上的例子,如“为Uber Jar选择内容”,但我仍然不能理解“着色”是如何完成的。

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


当前回答

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

其他回答

最近我一直在想,为什么elasticsearch会隐藏和重定位一些(但不是全部)依赖项。下面是项目维护者@kimchy的解释:

The shading part is intentional, the shaded libraries we use in elasticsearch are for all intent and purpose part of elasticsearch, the version used is tied closely into what elasticsearch exposes and how it uses the library based on the internals of how the library works (and that changes between versions), netty and guava are great examples. Btw, I have no problem with actually providing several jars of elasticsearch, one with lucene not shaded, and one with Lucene shaded. Not sure how to do it with maven though. I don't want to provide a version that does not shade netty/jackson for example, because of the deep intimiate usage elasticsearch has with them (for example, using the upcoming bufferring improvement with any previous version of netty except for the current one will actually use more memory compared to using considerably less).

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

另一个来自drewr:

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

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

So, that's one use case. As for an illustrative example, below is how maven-shade-plugin is used in elasticsearch's pom.xml (v0.90.5). The artifactSet::include lines instruct it what dependencies to pull into the uber JAR (basically, they are unzipped and and re-packaged alongside elasticsearch's own classes when the target elasticsearch jar is produced. (In case you didn't know this already, a JAR file is just a ZIP file that contains the program's classes, resources, etc., and some metadata. You can extract one to see how it's put together.)

relocations::relocation行类似,只是在每种情况下,它们还将指定的替换应用到依赖项的类中——在本例中,将它们放在org.elasticsearch.common下。

最后,过滤器部分从目标JAR中排除了一些不应该在那里的东西——例如JAR元数据、ant构建文件、文本文件等,这些文件与一些依赖项打包在一起,但不属于一个优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>

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


小的警告

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

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

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


Downlaod JarJar's JAR 文件

你可以从这里下载罐子: https://code.google.com/p/jarjar/ 在左边的菜单中,你可以找到一个下载链接。


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

在本例中,我们将从“com.fasterxml”更改包。杰克逊”到“杰克逊”。 源JAR名为“jackson-databind-2.6.4.jar”,新修改的(目标)JAR名为“kuku-jackson-databind-2.6.4.jar”。 -“jarjar”JAR文件是1.4版本

创建一个rules.txt文件。文件的内容应该是(注意'@'字符前的句点): com.fasterxml.jackson规则。* * io.kuku.dependencies.com.fasterxml.jackson .@1 执行如下命令: Java -jar jarjar-1.4.jar进程规则。txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.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>

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

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

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

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


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

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

For example, I am developing Foo library, which depends on a specific version (e.g. 1.0) of Bar library. Assuming I cannot make use of other version of Bar lib (because API change, or other technical issues, etc). If I simply declare Bar:1.0 as Foo's dependency in Maven, it is possible to fall into a problem: A Qux project is depending on Foo, and also Bar:2.0 (and it cannot use Bar:1.0 because Qux needs to use new feature in Bar:2.0). Here is the dilemma: should Qux use Bar:1.0 (which Qux's code will not work) or Bar:2.0 (which Foo's code will not work)?

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