1. 概要

マルチモジュールのMavenプロジェクトには、複雑な依存関係グラフを含めることができます。 これらは異常な結果をもたらす可能性があり、モジュールが相互にインポートする回数が多くなります。

このチュートリアルでは、Mavenでアーティファクトのバージョンの衝突を解決する方法を説明します。

マルチモジュールプロジェクトから始めます。このプロジェクトでは、同じアーティファクトの異なるバージョンを意図的に使用しています。 次に、除外または依存関係の管理を使用して、アーティファクトの間違ったバージョンを取得しないようにする方法を説明します。

最後に、m aven-enforcer-plugin を使用して、推移的な依存関係の使用を禁止することにより、制御を容易にすることを試みます。

2. アーティファクトのバージョン衝突

プロジェクトに含める各依存関係は、他のアーティファクトにリンクしている可能性があります。 Mavenは、推移的な依存関係とも呼ばれるこれらのアーティファクトを自動的に取り込むことができます。 バージョンの衝突は、複数の依存関係が同じアーティファクトにリンクしているが、異なるバージョンを使用している場合に発生します。

その結果、コンパイルフェーズと実行時の両方で、アプリケーションにエラーが発生する可能性があります。

2.1. プロジェクト構造

実験するマルチモジュールプロジェクト構造を定義しましょう。 私たちのプロジェクトは、version-collision親モジュールと3つの子モジュールで構成されています。

version-collision
    project-a
    project-b
    project-collision

project-aproject-bpom.xmlはほぼ同じです。 唯一の違いは、依存しているcom.google.guavaアーティファクトのバージョンです。 特に、project-aはバージョン22.0を使用します。

<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>22.0</version>
    </dependency>
</dependencies>

ただし、project-bは新しいバージョンの29.0-jreを使用します。

<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>29.0-jre</version>
    </dependency>
</dependencies>

3番目のモジュールproject-collisionは、他の2つに依存しています。

<dependencies>
    <dependency>
        <groupId>com.baeldung</groupId>
        <artifactId>project-a</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.baeldung</groupId>
        <artifactId>project-b</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

では、 guavaのどのバージョンがproject-collision で利用できるようになりますか?

2.2. 特定の依存関係バージョンの機能の使用

guavaFutures.immediateVoidFutureメソッドを使用するproject-collisionモジュールで簡単なテストを作成することで、どの依存関係が使用されているかを確認できます。

@Test
public void whenVersionCollisionDoesNotExist_thenShouldCompile() {
    assertThat(Futures.immediateVoidFuture(), notNullValue());
}

このメソッドは、29.0-jreバージョンからのみ使用できます。 これは他のモジュールの1つから継承していますが、project-b。から推移的な依存関係を取得した場合にのみコードをコンパイルできます。

2.3. バージョンの衝突によるコンパイルエラー

project-collision モジュールの依存関係の順序に応じて、特定の組み合わせでMavenはコンパイルエラーを返します。

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:testCompile (default-testCompile) on project project-collision: Compilation failure
[ERROR] /tutorials/maven-all/version-collision/project-collision/src/test/java/com/baeldung/version/collision/VersionCollisionUnitTest.java:[12,27] cannot find symbol
[ERROR]   symbol:   method immediateVoidFuture()
[ERROR]   location: class com.google.common.util.concurrent.Futures

これは、com.google.guavaアーティファクトのバージョンの衝突の結果です。 デフォルトでは、依存関係ツリーの同じレベルの依存関係に対して、Mavenは最初に検出したライブラリーを選択します。 この場合、 com.google.guava の両方の依存関係は同じ高さであり、古いバージョンが選択されています。

2.4. maven-dependency-pluginを使用する

maven-dependency-plugin は、すべての依存関係とそのバージョンを表示するための非常に便利なツールです。

% mvn dependency:tree -Dverbose

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision ---
[INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT
[INFO] +- com.baeldung:project-a:jar:0.0.1-SNAPSHOT:compile
[INFO] |  \- com.google.guava:guava:jar:22.0:compile
[INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile
[INFO]    \- (com.google.guava:guava:jar:29.0-jre:compile - omitted for conflict with 22.0)

-Dverbose フラグは、競合するアーティファクトを表示します。 実際、22.0と29.0-jreの2つのバージョンでcom.google.guavaの依存関係があります。 後者は、project-collisionモジュールで使用したいものです。

3. アーティファクトから推移的な依存関係を除外する

バージョンの衝突を解決する1つの方法は、特定のアーティファクトから競合する推移的な依存関係を削除することです。 この例では、com.google.guavaライブラリをプロジェクトから推移的に追加する必要はありません-アーティファクト。

したがって、 project-collisionpomで除外できます。

<dependencies>
    <dependency>
        <groupId>com.baeldung</groupId>
        <artifactId>project-a</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <exclusions>
            <exclusion>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.baeldung</groupId>
        <artifactId>project-b</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

ここで、dependentency:tree コマンドを実行すると、それがもう存在しないことがわかります。

% mvn dependency:tree -Dverbose

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision ---
[INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT
[INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile
[INFO]    \- com.google.guava:guava:jar:29.0-jre:compile

その結果、コンパイルフェーズはエラーなしで終了し、バージョン29.0-jreのクラスとメソッドを使用できます。

4. dependencyManagementセクションの使用

MavenのdependencyManagementセクションは、依存関係情報を一元化するためのメカニズムです。 その最も有用な機能の1つは、一時的な依存関係として使用されるアーティファクトのバージョンを制御することです。

そのことを念頭に置いて、親のpomdependencyManagement構成を作成しましょう。

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
         <version>29.0-jre</version>
      </dependency>
   </dependencies>
</dependencyManagement>

その結果、Mavenはすべての子モジュールでcom.google.guavaアーティファクトのバージョン29.0-jreを使用するようにします。

% mvn dependency:tree -Dverbose

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision ---
[INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT
[INFO] +- com.baeldung:project-a:jar:0.0.1-SNAPSHOT:compile
[INFO] |  \- com.google.guava:guava:jar:29.0-jre:compile (version managed from 22.0)
[INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile
[INFO]    \- (com.google.guava:guava:jar:29.0-jre:compile - version managed from 22.0; omitted for duplicate)

5. 偶発的な推移的な依存関係を防ぐ

maven-enforcer-plugin は、がマルチモジュールプロジェクトの管理を簡素化する多くの組み込みルールを提供します。 それらの1つは、推移的な依存関係からのクラスとメソッドの使用を禁止しています。

明示的な依存関係の宣言により、アーティファクトのバージョンの衝突の可能性が排除されます。 そのルールを使用してmaven-enforcer-pluginを親pomに追加しましょう。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.0.0-M3</version>
    <executions>
        <execution>
            <id>enforce-banned-dependencies</id>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <banTransitiveDependencies/>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

結果として、 project-collisionモジュールでcom.google.guavaアーティファクトを自分で使用する場合は、明示的に宣言する必要があります。 使用するバージョンを指定するか、親pom.xmldependencyManagementを設定する必要があります。 これにより、プロジェクトの間違いを防ぐことができますが、pom.xmlファイルでより明示的にする必要があります。

6. 結論

この記事では、Mavenでアーティファクトのバージョンの衝突を解決する方法を見てきました。

最初に、マルチモジュールプロジェクトでのバージョンの衝突の例を調べました。

次に、pom.xmlで推移的な依存関係を除外する方法を示しました。 親pom.xmldependencyManagementセクションを使用して依存関係のバージョンを制御する方法を確認しました。

最後に、 maven-enforcer-plugin を試して、各モジュールに独自の制御を強制するために、推移的な依存関係の使用を禁止しました。

いつものように、この記事に示されているコードは、GitHubから入手できます。