私は自分のキャリアの中でいくつかのビルドツールを見てきましたが、それらにはすべて癖があります。私はたった今Mavenを調べていて、「依存関係の除外」という考えに初めて出会いました。このメカニズムが解決しようとしている正当な問題が正直にわかりません。
たとえば、Apache PigのPOMを見てみましょう。
<project xmlns="http://maven.Apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>org.Apache.pig</groupId>
<artifactId>pig</artifactId>
<packaging>jar</packaging>
<version>0.16.0.2.5.0.0-1245</version>
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.Apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.7.4</version>
<exclusions>
<exclusion>
<!-- Don't pull in Avro's (later) version of Jetty. -->
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
</exclusion>
<exclusion>
<!--
Exclude Avro's version of ant since it conflicts with Jetty's.
-->
<groupId>org.Apache.ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ... -->
</dependencies>
<!-- ... -->
</project>
この特定のプロジェクトは、org.Apache.avro
グループのavro
アーティファクトに依存していますが、「競合」するため、その依存関係から2つの依存関係(つまり、jetty
およびant
)を削除します。どうやら、これらの依存関係をavro
から削除しても問題ありません。pig
に含まれている依存関係と同じです。
私には、これは問題を求めるように聞こえます。依存関係の競合を解決することは、それよりも優先されると思います。
依存関係の依存関係を変更することにより、PigプロジェクトはAvroプロジェクトに効果的にパッチを適用します。また、Avroのバージョンの制約がプロジェクトの1つのリリースを厳密に特定しないためです。
ビルドを完全に拒否するか、ant
とjetty
の2つの別々のバージョンをプロジェクトにビルドする方が理にかなっています(Javaのクラスローダーシステムで分離しておくことができると仮定した場合) )?
それが解決する2つの問題があり、どちらにも回避策がありますが、どちらの回避策も実際のプロジェクトでは常に利用できません。
しかし、最初に Maven依存関係メカニズム に慣れていない人のために:
このメカニズムは、望ましくない依存関係を提供する場合があるため、exclusion
ルールを使用して、Mavenが特定の推移的な依存関係を含めるのを防ぎます。
それでは、問題に移ります。
問題#1:望ましくない依存関係
Spring 3.xを使用してWebアプリを構築していて、さらに、Log4JアダプターでSLF4Jを使用しているとします。ただし、Spring 3.xはCommons-Loggingを内部で使用します。 SLF4Jには、Commons-Logging実装を置き換えるアダプターも含まれています。
ただし、Mavenが推移的な依存関係をWARに格納する特定の順序によっては、Springのロギングメッセージが実際にはCommons-Loggingの実装を経由している場合があります。関連するSpring依存関係に除外を追加することで、これが発生するのを防ぎます。
この問題を解決する正しい方法は、Springがその依存関係をprovided
としてマークすることであると私は主張します。これにより、メインプロジェクトまたは実行環境が依存関係を提供すると想定して、生成されたWARにMavenが自動的に組み込まれなくなります。残念ながら、おそらく依存関係の作成者にこれを行うように説得することはできないでしょう(そして多くの場合、それはnot正しいアプローチです)。そのため、必要な場所に除外を追加します。
問題#2:互換性のない推移的な依存関係
これはあなたが持っているように見えるケースです:jetty
に依存しているavro
のバージョンは、プロジェクトの他の部分との後方互換性がありません。 Javaコミュニティでは後方互換性が良いものであるため、これはよく使われるJavaアーティファクトでは一般的ではありません。しかし、バグ修正によってそれが不可能になる場合もあります。
これは、アプリケーションの作成者(つまり、あなた)が介入し、すべての依存関係が適切に機能することを確認する必要がある場合です。プロジェクトで古いバージョンのjetty
が必要な場合は、そのバージョンをサポートするavro
のバージョンを使用するか、はるかに優れている何でも更新する必要があります以前の動作に依存するアプリケーションで。
醜い回避策は、dependency-management
セクションを使用するか、依存関係を直接参照して、プロジェクトで依存関係のバージョンを明示的に指定することです。これらはどちらも推移的な依存関係の前提全体に違反していますが、...
これは、一般的な依存関係のバージョンを必要以上に狭く設定するサードパーティライブラリの問題を解決します。たとえば、実際には2.0以上で動作する可能性がありますが、要件は2.4+、またはより正確には2.4.3に設定されています。なぜこれを行うのですか?彼らは以前のバージョンでテストするためのリソースを持っていないかもしれません、そしてサポートを主張することに抵抗を感じません。多くの場合、彼らは単にそれ以上何も知らないだけです。理由が何であれ、それは野生で珍しい出来事ではありません。
明らかに、両方のライブラリーを制御できる場合は、他のライブラリーへの依存関係を修正することが最善の方法です。そうでない場合は、除外によってテストのリスクを取り、すべてを連携させることができます。
そして、いいえ、ライブラリの複数のバージョンは現実的ではありません。言語ランタイムが名前空間の衝突に対処できても対応できない場合でも、これら2つのバージョンが実際に相互作用する必要がある場合は問題が発生します。データ型とコードの異なるバージョン間の競合を解決する方法はありません。ライブラリの2つの別々のバージョンを使用する唯一の方法は、それらがマイクロサービスアーキテクチャのように2つの別々のプロセスにある場合です。