dependencyManagementとは?Mavenで依存関係を一元管理する方法を解説

Java

Mavenでプロジェクトを管理していると、「同じライブラリなのにバージョンがバラバラ…」という状況に陥ることがあります。

特に複数のモジュールを持つプロジェクトでは、バージョンの統一が大変ですよね。

そんな問題を解決してくれるのがdependencyManagement(依存関係管理)という機能です。

今回は、Mavenのpom.xmlで使える<dependencyManagement>について、通常の<dependencies>との違いから実践的な使い方まで、初心者の方にも分かりやすく解説していきます。


スポンサーリンク

dependencyManagementとは何か?

依存関係のバージョンを一元管理する仕組み

dependencyManagementは、Mavenで使用するライブラリ(依存関係)のバージョンを集中管理するための機能です。

pom.xmlの<dependencyManagement>セクションに記述することで、プロジェクト全体で使用するライブラリのバージョンを統一できます。

イメージで理解する:

通常の<dependencies>が「このライブラリを実際に使います」という宣言なら、<dependencyManagement>は「このライブラリを使う場合は、このバージョンを使ってね」というルールブックのようなものです。

dependenciesとの違い

この2つは似ているようで、明確な違いがあります。

<dependencies>

  • 実際に依存関係を追加する
  • プロジェクトに直接影響する
  • ビルド時にダウンロードされる

<dependencyManagement>

  • 依存関係のバージョン情報を定義するだけ
  • それ自体では何もダウンロードされない
  • 子プロジェクトや<dependencies>で参照される

具体例で比較:

<!-- dependencyManagement:定義だけ -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- dependencies:実際に使用 -->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <!-- バージョン不要!上で定義されている -->
    </dependency>
</dependencies>

<dependencyManagement>でバージョンを定義しておけば、<dependencies>ではバージョン番号を書かなくても良いんです。


なぜdependencyManagementが必要なのか?

バージョンの統一が困難

複数のモジュールを持つプロジェクトでは、同じライブラリを複数箇所で使います。

dependencyManagementなしの場合:

<!-- module-a/pom.xml -->
<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>30.1-jre</version>
    </dependency>
</dependencies>

<!-- module-b/pom.xml -->
<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version> <!-- バージョンが違う! -->
    </dependency>
</dependencies>

各モジュールで個別にバージョンを指定すると、統一が難しくなります。

バージョンアップの手間

ライブラリを更新する際、すべてのpom.xmlを修正する必要があります。

問題点:

  • 修正箇所が多い
  • 更新漏れが発生しやすい
  • バージョン不整合が起きやすい

バージョン競合の発生

異なるバージョンが混在すると、予期しない動作やエラーが発生することがあります。

dependencyManagementを使うと:

  • 親pom.xmlで一度だけバージョンを定義
  • 子モジュールではバージョン指定不要
  • バージョンアップも一箇所を変更するだけ

管理が劇的に楽になります。


基本的な使い方

単一プロジェクトでの使用

シンプルなプロジェクトでも、dependencyManagementは有効です。

pom.xmlの例:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0.0</version>

    <!-- バージョン情報をプロパティで定義 -->
    <properties>
        <spring.version>5.3.20</spring.version>
        <junit.version>4.13.2</junit.version>
    </properties>

    <!-- 依存関係のバージョンを管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 実際に使用する依存関係 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <!-- バージョン不要 -->
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <!-- バージョン不要 -->
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <!-- バージョン不要 -->
        </dependency>
    </dependencies>
</project>

メリット:

  • バージョンが一箇所にまとまって見やすい
  • バージョンアップが簡単
  • プロパティと組み合わせると更に便利

マルチモジュールプロジェクトでの活用

親子構造での使用

複数のモジュールを持つプロジェクトで真価を発揮します。

プロジェクト構造:

parent-project/
├── pom.xml (親POM)
├── module-a/
│   └── pom.xml
├── module-b/
│   └── pom.xml
└── module-c/
    └── pom.xml

親POMの設定

parent-project/pom.xml:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <!-- 子モジュールの定義 -->
    <modules>
        <module>module-a</module>
        <module>module-b</module>
        <module>module-c</module>
    </modules>

    <!-- バージョン情報 -->
    <properties>
        <spring-boot.version>3.0.0</spring-boot.version>
        <lombok.version>1.18.24</lombok.version>
        <junit.version>5.9.0</junit.version>
    </properties>

    <!-- 依存関係の統一管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot BOM -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- その他のライブラリ -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

子モジュールの設定

module-a/pom.xml:

<project>
    <modelVersion>4.0.0</modelVersion>

    <!-- 親POMを継承 -->
    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>module-a</artifactId>

    <!-- 依存関係の追加(バージョン不要) -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- バージョンは親POMで管理 -->
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!-- バージョンは親POMで管理 -->
        </dependency>
    </dependencies>
</project>

module-b/pom.xml:

<project>
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>module-b</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <!-- 同じSpring Bootバージョンが適用される -->
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

すべての子モジュールで、統一されたバージョンが自動的に適用されます。


BOM(Bill of Materials)の活用

BOMとは

BOM(Bill of Materials)は、「部品表」という意味で、関連するライブラリのバージョンをまとめて定義したpom.xmlのことです。

Spring BootやSpring Cloudなど、多くのフレームワークがBOMを提供しています。

BOMのインポート

<scope>import</scope><type>pom</type>を使って、BOMをインポートできます。

例:Spring Boot BOMの使用

<dependencyManagement>
    <dependencies>
        <!-- Spring Boot BOMをインポート -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- バージョン指定不要!BOMで管理されている -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
</dependencies>

複数のBOMを組み合わせ

複数のBOMを同時にインポートすることもできます。

<dependencyManagement>
    <dependencies>
        <!-- Spring Boot BOM -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- Spring Cloud BOM -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2022.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- AWS SDK BOM -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.19.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

各BOMで定義されているライブラリを、すべてバージョン指定なしで使えます。


バージョンの上書き

dependencyManagementで定義したバージョンを変更

特定のモジュールだけ異なるバージョンを使いたい場合、明示的にバージョンを指定できます。

親POM:

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

子POM:

<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version> <!-- 上書き -->
    </dependency>
</dependencies>

子POMで明示的にバージョンを指定すると、親の定義より優先されます。

exclusionとの組み合わせ

特定の推移的依存関係を除外することもできます。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</dependencyManagement>

TomcatではなくJettyを使いたい場合などに便利です。


バージョン競合の解決

依存関係ツリーの確認

どのバージョンが実際に使われているか確認できます。

コマンド:

mvn dependency:tree

出力例:

[INFO] com.example:my-app:jar:1.0.0
[INFO] +- org.springframework:spring-core:jar:5.3.20:compile
[INFO] |  \- org.springframework:spring-jcl:jar:5.3.20:compile
[INFO] \- junit:junit:jar:4.13.2:test

依存関係の階層構造が表示されます。

バージョン競合の検出

mvn dependency:tree -Dverbose

競合している依存関係が詳しく表示されます。

dependencyManagementで解決

競合するライブラリを<dependencyManagement>で明示的に指定します。

<dependencyManagement>
    <dependencies>
        <!-- 競合しているライブラリのバージョンを固定 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
</dependencyManagement>

これで、すべてのモジュールで統一されたバージョンが使われます。


ベストプラクティス

プロパティでバージョン管理

バージョン番号をプロパティとして定義すると、変更が容易になります。

<properties>
    <!-- Java バージョン -->
    <java.version>17</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>

    <!-- ライブラリバージョン -->
    <spring-boot.version>3.0.0</spring-boot.version>
    <lombok.version>1.18.24</lombok.version>
    <jackson.version>2.14.0</jackson.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

scopeも一緒に定義

バージョンだけでなく、scopeも<dependencyManagement>で定義できます。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope> <!-- scopeも定義 -->
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

子POMでは、groupIdとartifactIdだけ書けばOKです。

BOMを積極的に活用

主要なフレームワークはBOMを提供しているので、活用しましょう。

メリット:

  • 相互に互換性のあるバージョンが保証される
  • 個別にバージョンを調べる手間が不要
  • フレームワークの推奨設定が適用される

Gradleでの同等機能

MavenのdependencyManagementに相当する機能は、GradleではプラットフォームBOMとして提供されます。

Gradleの例:

// build.gradle
dependencies {
    // BOMをインポート
    implementation platform('org.springframework.boot:spring-boot-dependencies:3.0.0')

    // バージョン指定不要
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    testImplementation 'org.junit.jupiter:junit-jupiter'
}

Kotlinの場合:

// build.gradle.kts
dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:3.0.0"))

    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")

    testImplementation("org.junit.jupiter:junit-jupiter")
}

よくある質問

dependencyManagementは必須?

いいえ、必須ではありません。

ただし、以下の場合は強く推奨されます:

  • マルチモジュールプロジェクト
  • 複数のライブラリを統一的に管理したい
  • チーム開発
  • 大規模プロジェクト

親POMがない場合は使えない?

単一プロジェクトでも使えます。

親子関係がなくても、同じpom.xml内で<dependencyManagement><dependencies>を併用できます。

BOMと直接バージョン指定、どちらが優先?

優先順位:

  1. <dependencies>で明示的に指定したバージョン(最優先)
  2. <dependencyManagement>で指定したバージョン
  3. 推移的依存関係で引き込まれたバージョン

明示的な指定が常に優先されます。

複数のBOMで競合したら?

先に定義されたBOMが優先されます。

<dependencyManagement>
    <dependencies>
        <!-- これが優先される -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- 競合した場合、こちらは無視される -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

まとめ:dependencyManagementで効率的な依存関係管理を

dependencyManagementは、Mavenプロジェクトのライブラリバージョンを一元管理するための強力な機能です。

特にマルチモジュールプロジェクトでは必須と言えます。

この記事のポイント:

  • dependencyManagementは依存関係のバージョンを定義する場所
  • 通常のdependenciesとは異なり、それ自体はダウンロードしない
  • 親POMで定義し、子モジュールで参照する
  • BOMをインポートすると多数のライブラリを一括管理できる
  • プロパティと組み合わせると更に便利
  • バージョン競合を防ぎ、統一的な管理が可能
  • scopeや除外設定も定義できる
  • 子POMで明示的に指定すれば上書き可能
  • dependency:treeで依存関係を確認
  • Gradleではplatformを使用

最初のステップ:

  1. 既存のpom.xmlを確認
  2. dependencyManagementセクションを追加
  3. バージョン情報をプロパティに移動
  4. dependenciesからバージョン指定を削除

dependencyManagementを使いこなせば、プロジェクトの保守性が格段に向上します。

最初は少し面倒に感じるかもしれませんが、プロジェクトが大きくなるほど、その恩恵を実感できますよ。

効率的な依存関係管理で、快適な開発ライフを実現しましょう!

コメント

タイトルとURLをコピーしました