「影付き」Java依存関係とは何ですか?


75

JVM開発者はこちら。最近、私はIRCチャットルームや、自分のオフィスでさえ、いわゆる「影付き」Javaライブラリについて冗談を言っています。使用のコンテキストは次のようになります。

このように、XYZに「シェーディング」クライアントを提供します。

完璧な例は、HBaseのこのJiraの問題です。「シェーディングされた依存関係を持つクライアントアーティファクトを公開する

だから私は尋ねる:シェーディングされた JAR とは何ですか、「シェーディングされた」とはどういう意味ですか?

回答:


88

依存関係のシェーディングとは、依存関係含めて名前を変更し(クラスを再配置し、影響を受けるバイトコードとリソースを書き換える)、独自のコードとともにバンドルするプライベートコピー作成するプロセスです

この概念は通常、uber-jar(別名fat jar)に関連付けられています

mavenシェードプラグインは、その単一の名前で2つのこと(独自のページを引用する)を行うため、用語について多少の混乱があります。

このプラグインは、依存関係を含むuber-jarにアーティファクトをパッケージ化し、いくつかの依存関係のパッケージをシェーディング(つまり、名前変更)する機能を提供します。

したがって、シェーディング部分は実際にはオプションです。プラグインは、依存関係をjar(ファットjar)に含めることを許可し、オプションで依存関係の名前を変更(shade)することもできます。

別のソースを追加する:

ライブラリをシェーディングするには、ライブラリのコンテンツファイルを取得し、独自のjarに入れて、パッケージを変更します。これは、ライブラリファイルを別のパッケージに再配置せずに、独自のjar内で単に配布するパッケージングとは異なります。

技術的に言えば、依存関係は影付きです。しかし、fat-jar-with-shaded-dependenciesを「シェーディングjar」と呼ぶのが一般的であり、そのjarが別のシステムのクライアントである場合、「shaded client」と呼ぶことができます。

ここでタイトル、あなたの質問にリンクされHBaseのためのJIRA課題のは:

影付きの依存関係を持つクライアントアーティファクトを公開する

したがって、この投稿では、2つの概念を混同することなく提示しようとしています。

いいもの

Uber-jarは、多くの場合、アプリケーションを単一のファイルとして出荷するために使用されます(デプロイと実行が簡単になります)。それらは、他のアプリケーション(異なるバージョンのライブラリを使用する可能性がある)で使用される場合の競合を回避するために、シェーディングされた依存関係の一部(またはすべて)とともにライブラリを出荷するためにも使用できます。

uber-jarを構築するにはいくつかの方法がありますがmaven-shade-pluginクラスの再配置機能をさらに一歩進めます。

uber JARが他のプロジェクトの依存関係として再利用される場合、アーティファクトの依存関係からのクラスをuber JARに直接含めると、クラスパス上のクラスが重複するため、クラスロードの競合が発生する可能性があります。この問題に対処するには、シェーディングされたアーティファクトに含まれるクラスを再配置して、バイトコードのプライベートコピーを作成します。

(履歴ノート:Jar Jar Linksは以前にその再配置機能を提供していました)

そのため、APIでライブラリのクラスを公開しない限り、ライブラリの依存関係を実装の詳細することができます。

DecayingSyncQuantanizerクラスを提供し、Apache commons-rngに依存するプロジェクト、ACME Quantanizer™があるとしましょう(もちろん、適切に量子化するために必要なのは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プラグインはdependency-reduced-pom.xml、シェーディングされた依存関係が<dependencies>セクションから削除される新しいPOM()も生成します。これは、シェーディングされたjarを別のプロジェクトの依存関係として使用するのに役立ちます。そのため、ベースjarの代わりにそのjarを公開できます(または、シェーディングされたjarの修飾子を使用して)。

それは非常に便利です...

悪い人

...しかし、それは多くの問題ももたらします。jar内のすべての依存関係を単一の「名前空間」に集約すると、面倒になり、リソースのシェーディングと混乱が必要になります。

たとえば、クラス名またはパッケージ名を含むリソースファイルを処理する方法は?こうしたすべてがの下で暮らすサービスプロバイダ記述子などのリソースファイルMETA-INF/services

シェードプラグインは、それを支援するリソーストランスフォーマーを提供します。

複数のアーティファクトから1つのuber JARにクラス/リソースを集約することは、重複がない限り簡単です。それ以外の場合は、複数のJARからリソースをマージするための何らかの種類のロジックが必要です。これは、リソーストランスフォーマーが起動する場所です

しかし、それはまだ厄介であり、問​​題を予測することはほぼ不可能です(実稼働環境では難しい問題を頻繁に発見します)。why-we-stopped-building-fat-jarsを参照してください。

全体として、スタンドアロンのアプリ/サービスとしてファットjarを展開することは依然として非常に一般的であり、注意点を知っておく必要があります。また、一部のシェーディングやその他のトリックが必要場合もあります。

ぶさいく

さらに多くの困難な問題があります(デバッグ、テスト容易性、OSGiおよびエキゾチックなクラスローダーとの互換性...)。

しかし、もっと重要なのは、ライブラリを作成するとき、制御できると思っていたさまざまな問題が無限に複雑になっていることです。これは、jarが多くの異なるコンテキストで使用されるためです(スタンドアロンアプリ/サービスとしてデプロイするファットjarとは異なります)制御された環境で)。

たとえば、ElasticSearchは出荷したjarファイルの一部の依存関係をシェーディングするために使用していましたが、それをやめることにしました

バージョン2.0より前のElasticsearchは、同じアーティファクト内でシェーディングおよびパッケージ化されたいくつかの(すべてではない)共通の依存関係を持つJARとして提供されていました。これにより、独自のアプリケーションにElasticsearchを組み込んだJavaユーザーは、Guava、Joda、Jacksonなどのモジュールのバージョンの競合を回避することができました。
残念ながら、シェーディングは複雑でエラーが発生しやすいプロセスであり、一部の人にとっては問題を解決し、他の人にとっては問題を作成します。パッケージはビルド中に名前が変更されるため、シェーディングは開発者とプラグイン作成者がコードを適切に記述およびデバッグすることを非常に困難にします。最後に、以前はElasticsearchをシェーディングなしでテストしてからシェーディングされたjarを出荷していましたが、テストしていないものは出荷しません。
2.0からシェーディングなしでElasticsearchを出荷することにしました。

シェーディングされたjarではなく、シェーディングされた依存関係も参照していることに注意してください


1
これを説明してくれてありがとう。Mavenシェードプラグインの公式ドキュメントは完全に不適切であり、これについては説明していません。また、「uber jar」を定義する手間さえありません。その文書は鈍くて役に立たない。あなたの記事は役に立ちます。
チーソー

優れた説明、公式ドキュメントに含まれるべきだと思います
Adelin

7

少なくともmavenを使用する場合は、シェーディングされたjarの作成に実際に関与するソフトウェアの助けを借りて質問に答えさせてください。

Apache Maven Shadeプラグインのホームページから取得

このプラグインは、依存関係を含むアーバーアーティファクトをuber-jarにパッケージ化し、一部の依存関係のパッケージをシェーディング(つまり、名前変更)する機能を提供します。

シェードjar別名uber-jar別名fat jarには、デフォルトでJavaアプリケーションの実行に必要なすべての依存関係が含まれるため、クラスパスに追加の依存関係は必要ありません。アプリケーションを実行するには、正しいJavaバージョンのみが必要です。シェーディングされたjarは、デプロイメント/クラスパスの問題を回避するのに役立ちますが、元のアプリケーションjarよりもはるかに大きくなり、jar hellを回避するのに役立ちません。


1
この答えは不完全であると恐れています。ファット/ユーバージャーとは何かを説明していますが、シェーディング部分については説明していません。そして、はい、シェーディングは100% "jar hell"を支援することになっています(その答えの最後の部分が間違っています)。あるレベルでは有用ですが、混乱を招くことになります:-/
Hugues M.

1
@HuguesMoreau私の回答は100%完全ではないかもしれませんが、それでも私がやりたいと思ったポイントをもたらしました。足りない部分をテーブルに持って来てくれてありがとう。シェーディングはjar hellを回避しません。これは私が意図し、書いたものですが、問題を解決できるツールを提供しますが、自動ではありません。少なくとも私が意図したとおりに読んで解釈すると、最後の部分になります、少なくとも大丈夫です。:)
ジェスコR.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.