多くの依存関係を持つMavenプロジェクトを構築する場合、それらの依存関係の一部は同じライブラリに依存しますが、アプリケーションの実行時にエラーの原因となる異なるバージョンを使用します。
たとえば、Apache commons httpクライアントに依存する2つの異なるプロジェクト依存関係を追加し、それぞれが異なるバージョンにある場合、クラスローダーがAのApache commons httpクライアントクラスをロードすると、Bはそれらを使用しようとしますそれらはすでにクラスローダーによってロードされています。
しかし、Bのバイトコードは、ロードされたクラスの異なるバージョンに依存しているため、アプリケーションの実行時に複数の問題が発生します。一般的なものはmethodnotfound例外です(Aのバージョンのhttpクライアントは特定のメソッドを使用しなくなったため)。
そのような競合を避けるために構築するときの一般的な戦略は何ですか?依存関係ツリーを手動で確認して、どの共通ライブラリが相互に衝突するかを把握する必要がありますか?
mavenの依存関係の地獄 へようこそ。これは、プロジェクトが成長し、より多くの外部依存関係が導入されるにつれて、やや一般的な問題です。
Apache Commons(元の質問で述べた)に加えて、ロギングフレームワーク(log4j、slf4j)もよくある犯人です。
競合が特定されたら、それを解決する方法に関する「マット」のアドバイスに同意します。これらのバージョンの競合を早期に発見するという観点から、maven「エンフォーサー」プラグインを使用することもできます。 "dependencyConvergence" config を参照してください。 this SO post も参照してください。
施行プラグインを使用すると、バージョンの競合が発生するとすぐにビルドが失敗するため、手動でのチェックが不要になります。これは積極的な戦略ですが、質問/投稿を促す実行時の問題のタイプを防ぎます。何でもそうですが、施行プラグインには長所と短所があります。私たちは昨年中にそれを使い始めましたが、それが祝福と呪いになり得ることを発見しました。 libs/frameworksの多くのバージョンは後方互換性があるため、バージョン1.2.3と1.2.4の両方に(直接的または間接的に)依存することは、多くの場合、コンパイル時とランタイムの両方で問題ありません。ただし、エンフォーサプラグインはこの競合にフラグを立て、必要なバージョンを正確に宣言する必要があります。依存関係の競合の数が少ないと仮定すると、これは多くの作業を必要としません。ただし、大きなフレームワーク(Spring MVCなど)を導入すると、厄介になる可能性があります。
うまくいけば、それは役に立つ情報です。
Maven依存プラグインの tree
goal を使用して、プロジェクト内のすべての推移的な依存関係を表示し、「省略された」という依存関係を探すことができます。競合の場合」 1
mvn dependency:tree -Dverbose
mvn dependency:tree -Dverbose | grep 'omitted for conflict'
どの依存関係にバージョンの競合があるかがわかったら、includes
パラメーターを使用して、その依存関係につながる依存関係のみを表示し、特定の依存関係がどのように取り込まれるかを確認できます。 AとBによって引き込まれる:
mvn dependency:tree -Dverbose -Dincludes=project-c
[INFO] com.my-company:my-project:jar:1.0-SNAPSHOT
[INFO] +- project-a:project-a:jar:0.1:compile
[INFO] | \- project-c:project-c:jar:1.0:compile
[INFO] \- project-b:project-b:jar:0.2:compile
[INFO] \- project-x:project-x:jar:0.1:compile
[INFO] \- (project-c:project-c:jar:2.0:compile - omitted for conflict)
実際に競合を解決するために、場合によっては、両方の主要な依存関係が機能する推移的な依存関係のバージョンを見つけることができる場合があります。 pomのdependencyManagement
セクションに推移的な依存関係を追加し、機能するまでバージョンを変更してみてください。
ただし、他の場合では、すべての人に機能する依存関係のバージョンを見つけることができない場合があります。このような場合、すべてのユーザーに有効な推移的な依存関係のバージョンを使用するために、主な依存関係の1つでバージョンを戻す必要がある場合があります。たとえば、上記の例では、A 0.1はC 1.0を使用し、B 0.2はC 2.0を使用します。 C 1.0と2.0は完全に互換性がないと仮定します。しかし、プロジェクトで代わりにB 0.1を使用することもできます。これは、C 1.0と互換性のあるC 1.5に依存する場合があります。
もちろん、これらの2つの戦略は常に機能するとは限りませんが、私は以前に成功を収めました。他のより抜本的なオプションには、非互換性を修正する独自のバージョンの依存関係をパッケージ化するか、2つの依存関係を別々のクラスローダーで分離しようとすることが含まれます。
Pomでmaven-enforcer-pluginを使用して、特定のバージョンの推移的な依存関係を強制できます。これは、競合がある場合に、pom設定による脱落を防ぐのに役立ちます。
これが私のために働いたものであり、私は一致するようにバージョンを変更することができました。バージョンを変更できない場合、これはあまり役に立ちません。
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.Apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
...
</project>
括弧を使用して依存関係のバージョンを強制します。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
<version>[1.0.0]</version>
</dependency>
トッドとマットの答えを、次のことができるという事実で拡張したいと思います。
mvn dependency:tree -Dverbose -Dincludes=project-c
<exclusions/>
の推移的な依存関係を持つすべての依存関係にproject-c
タグを追加します。
または、プロジェクト内で、推移的なものをオーバーライドして競合を回避するために、依存関係として明示的にproject-c
を定義します。 (これは `-Dverboseを使用している場合でもツリーに表示されます)。
または、これらのプロジェクトが管理下にある場合は、project-c
のバージョンを単純にアップグレードできます。