JVM开发人员在这里。最近,我在IRC聊天室甚至在我自己的办公室里看到了所谓的“阴影” Java库的玩笑。使用的上下文将类似于:


“这样为XYZ提供了一个“阴影”客户端。”


完美示例是Hira的Jira问题:“发布具有阴影依赖项的客户端工件”

所以我问:什么是阴影JAR,被“阴影”意味着什么? />

#1 楼

着色依赖项是包含和重命名依赖项(从而重新定位类并重写受影响的字节码和资源)以创建与您自己的代码捆绑在一起的私有副本的过程。 -jars(又名胖罐子)。

由于Maven阴影插件,该术语有些混乱,在该名称下,它做两件事(引用自己的页面):


该插件提供了将工件打包在uber-jar中的功能,包括其依赖项以及对某些依赖项的包进行着色(即重命名)的功能。


所以阴影部分实际上是可选的:该插件允许在jar(胖jar)中包含依赖项,并可以选择重命名(阴影)依赖项。

添加另一个源: />
要遮蔽库,就是要获取该库的内容文件,将其放在您自己的jar中,然后更改其包。这与打包不同,打包只是将库文件放在您自己的jar中,而无需将它们重新定位到其他包中。


从技术上讲,依赖项被阴影化。但是通常将具有阴影依赖关系的胖罐子称为“阴影罐子”,如果该罐子是另一个系统的客户端,则可以称为“阴影客户机”。 />这是您在问题中链接的HBase的Jira问题的标题:


发布具有阴影依赖项的客户端工件


因此,在这篇文章中,我试图介绍这2个概念而不将它们混为一谈。使其易于部署和运行)。它们还可以用于将库及其部分(或全部)阴影依赖项一起运送,以避免在其他应用程序(可能使用这些库的不同版本)使用时发生冲突。

有多种构建uber-jars的方法,但是maven-shade-plugin凭借其类重定位功能更进一步:


如果将uber JAR用作其他项目的依赖项,则直接包括uber JAR中来自工件依赖项的类可能会由于类路径上重复的类而导致类加载冲突。为了解决这个问题,可以重定位阴影工件中包含的类,以创建其字节码的私有副本。


(历史记录:Jar Jar Links提供了该重定位

因此,除非您在API中公开那些库中的类,否则您就可以使它的库依赖关系成为实现细节。

我有一个项目, ACME Quantanizer™,提供DecayingSyncQuantanizer类,并且依赖于Apache commons-rng(因为要正确量化您需要XorShift1024Star,duh)。

如果我使用shade maven插件来生成uber -jar,然后查看内部,看到了这些类文件:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class
现在,如果我使用类重定位功能:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>


uber-jar的内容如下所示: ,我自己的班级和公地-rng cl

此外,Shade插件还将生成一个新的POM(dependency-reduced-pom.xml),其中从<dependencies>部分中删除了阴影依赖项。这有助于将着色的jar用作另一个项目的依赖项。因此,您可以发布该jar而不是基础jar或同时发布两者(使用带阴影的jar的限定符)。

因此非常有用...不好

...但是它也带来了许多问题。将所有依赖项聚合到jar内的单个“命名空间”中可能会变得混乱,并且需要对资源进行着色和处理。

例如:如何处理包含类名或包名的资源文件?资源文件(例如服务提供商描述符)都位于META-INF/services下?

shade插件提供了资源转换器,可以帮助您解决以下问题:只要没有重叠,将几个工件合并到一个超级JAR中就可以了。否则,需要某种逻辑来合并来自多个JAR的资源。这就是资源转换器的作用。


但它仍然很混乱,问题几乎是无法预料的(很多时候,您很难在生产中发现问题)。请查看为什么我们停止构建胖罐子。

总而言之,将胖子罐作为独立的应用程序/服务进行部署仍然非常普遍,您只需要了解这些陷阱,某些情况下,您可能需要着色或其他技巧。 ...您在受控环境中作为独立应用程序/服务部署的胖jar)。

例如,ElasticSearch曾在其出厂的jar中隐藏了一些依赖项,但他们决定停止这样做:


在2.0版之前,Elasticsearch是作为JAR提供的,其中一些(但不是全部)常见的依赖项被阴影化并打包在同一工件中。这有助于将Elasticsearch嵌入其自己的应用程序中的Java用户避免了诸如Guava,Joda,Jackson等模块的版本冲突。当然,仍然存在诸如Lucene之类的其他不受遮挡的依赖项列表,它们仍然可能引起冲突。不幸的是,着色是一个复杂且容易出错的过程,它可以解决某些人的问题,同时又为其他人带来问题。阴影使开发人员和插件作者很难正确编写和调试代码,因为在构建过程中会重命名软件包。最后,我们曾经在没有阴影的情况下测试Elasticsearch,然后再运送有阴影的jar,而且我们不希望运送任何我们未测试的东西。
我们决定从2.0开始不带阴影地运送Elasticsearch。


请注意,它们也指的是带阴影的依赖项,而不是带阴影的jar

评论


感谢您抽出宝贵的时间对此进行解释。 Maven Shade插件的官方文档是完全不够的,并且不讨论任何一个,甚至不愿意定义“超级jar”。该文档是晦涩且无用的。您的书面记录很有用。

– Cheeso
19年5月14日在20:47

很好的解释,我认为它应该包含在官方文档中

–阿德林
19年8月30日在8:44

这是一个递增的答案!包含理解它所需的所有详细信息,但不仅仅包含必要的信息,还包括大量的现场示例!

– Marco Freudenberger
20-1-23在9:47

#2 楼

让我在实际上负责创建阴影罐子的软件的帮助下回答问题……至少在使用maven时。

来自Apache Maven Shade插件主页: />
该插件提供了将工件打包(包括其依赖项)并着色(即重命名)某些依赖项包的功能。


默认情况下,带阴影的jar或uber-jar aka fat jar将包含运行Java应用程序所需的每个依赖关系,因此在类路径中不需要其他依赖关系。您只需要正确的Java版本即可运行您的应用程序。

评论


害怕这个答案是不完整的:它解释了什么是胖子/油罐,但没有解释阴影部分。是的,阴影应该100%有助于解决“地狱”(这会使答案的最后一部分不正确)。因此它在某种程度上很有用,但会增加混乱:-/

– Hugues M.
17年6月19日9:00

@HuguesMoreau我的回复可能不是100%完成,但仍然提出了我想讲的重点。感谢您将缺少的零件拿到桌子上。阴影不会避免震撼,这就是我的意思和所写的,但是它将为您提供一些工具,让您解决一些问题,但不是自动的。如果阅读和解释了我的意思,这就是最后一部分,至少可以。 :)

– Jesko R.
17年6月21日在12:57