什么是“阴影” Java依赖项?


74

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

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

完美的例子是针对HBase的Jira问题:“ 发布具有阴影依赖项的客户端工件

所以我问:什么是阴影 JAR,“阴影”是什么意思?

Answers:


86

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

该概念通常与uber-jar(又名胖罐)相关联。

由于maven shade plugin,该术语存在一些混淆,在该单一名称下,它做了两件事(引用自己的页面):

该插件提供了将工件打包(包括其依赖项)并遮蔽(即重命名)某些依赖项的包的功能。

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

添加另一个来源

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

从技术上讲,依赖项已被阴影化。但是通常将带有阴影的胖子依赖项称为“阴影罐”,如果该jar是另一个系统的客户端,则可以将其称为“阴影客户端”。

这是您在问题中链接的HBase的Jira问题的标题

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

因此,在这篇文章中,我试图不将它们混为一谈地介绍这两个概念。

善良

Uber-jars通常用于将应用程序作为单个文件发布(使其易于部署和运行)。它们也可以用来与它们的依赖的部分(或全部)沿着船库阴影,以便被其他应用程序(可能使用不同版本的库)使用时避免冲突。

有多种构建uber-jar的方法,但maven-shade-plugin通过其类重定位功能又走了一步:

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

(历史记录:Jar Jar Links之前提供了该重定位功能)

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

比方说,我有一个项目,ACME Quantanizer™,提供DecayingSyncQuantanizer一流的,并且依赖于Apache的公地RNG(因为当然是正确quantanize你需要XorShift1024Star,杜)。

如果我使用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的内容如下所示:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

这不仅是重命名文件,它还会重写引用重定位类的字节码(因此,我自己的类和commons-rng类都已转换)。

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

所以这可能非常有用...

坏人

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

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

shade插件提供了资源转换器,可以帮助您:

只要没有重叠,将来自多个工件的类/资源聚合到一个超级JAR中就很简单。否则,需要某种逻辑来合并来自多个JAR的资源。这就是资源转换器的作用。

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

总而言之,将胖子jar部署为独立的应用程序/服务仍然很常见,您只需要了解这些陷阱,对于某些陷阱,可能需要阴影或其他技巧。

丑陋的

还有许多困难的问题(调试,可测试性,与OSGi和奇异类加载器的兼容性...)。

但更重要的是,当您生成一个库时,您认为可以控制的各种问题现在变得越来越复杂,因为您的jar将在许多不同的上下文中使用(不同于您将胖jar部署为独立的应用程序/服务在受控环境中)。

例如,ElasticSearch曾经在它们运送的罐子中添加一些依赖项,但是他们决定停止这样做

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

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


1
感谢您抽出宝贵的时间对此进行解释。Maven Shade插件的官方文档是完全不够的,并且没有讨论其中的任何内容,甚至不费心定义“超级罐子”。该文档是晦涩且无用的。您的书面记录很有用。
Cheeso

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

7

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

取自Apache Maven Shade插件主页:

该插件提供了将工件打包(包括其依赖项)并遮蔽(即重命名)某些依赖项的包的功能。

默认情况下,带阴影的jar或uber-jar aka fat jar将包含运行Java应用程序所需的每个依赖关系,因此在类路径中不需要其他依赖关系。您只需要正确的Java版本即可运行您的应用程序。带阴影的jar将有助于避免部署/类路径问题,但是它将比原始应用程序jar大得多,并且无法帮助您避免jar地狱。


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

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