Article

📦 MavenのdependencyManagementとdependenciesの違いを整理する

maven, java, backend, dependency

Mavenのpom.xmlで混乱しやすいのが、<dependencies>と<dependencyManagement>の違いです。

役割は次のように分かれます。

  • dependencies: 実際にそのプロジェクトで使う依存ライブラリを追加する
  • dependencyManagement: バージョンやscopeなどの管理ルールを定義する

特にマルチモジュール構成では、この違いを理解しておくと依存関係の更新や競合調査がかなり楽になります。

dependenciesは実際にライブラリを追加する

<dependencies>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>example-client</artifactId>
    <version>1.2.3</version>
  </dependency>
</dependencies>

この記述では、example-clientが実際の依存関係に追加されます。

Mavenでは、直接指定したライブラリがさらに別ライブラリへ依存している場合、その依存も自動で取り込まれます。これを**推移的依存関係(transitive dependency)**と呼びます。

dependencyManagementは追加しない

次の記述だけでは、example-clientは依存関係へ追加されません。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>example-client</artifactId>
      <version>1.2.3</version>
    </dependency>
  </dependencies>
</dependencyManagement>

ここでは「このライブラリを使うなら1.2.3を使う」という管理情報だけを定義しています。

実際に使うモジュールでは、次のようにdependenciesへ追加します。

<dependencies>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>example-client</artifactId>
  </dependency>
</dependencies>

バージョンを省略できるのは、親POMや現在のPOMのdependencyManagementで管理されているためです。

マルチモジュールで中央管理する

例えば次の構成を考えます。

parent
├── api
├── batch
└── common

複数モジュールが同じライブラリを使う場合、各pom.xmlに同じバージョンを書くと更新漏れが起こりやすくなります。

親POMで一元管理します。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.x.x</version>
    </dependency>
  </dependencies>
</dependencyManagement>

子モジュールは必要な依存だけ宣言します。

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
</dependency>

これで変更箇所を親POMへ集約できます。

推移的依存のバージョンも管理できる

Mavenは同じartifactの複数バージョンが依存グラフに現れた場合、基本的にnearest definition、つまりプロジェクトに近い定義を優先してバージョンを解決します。同じ深さなら先に宣言されたものが優先されます。

dependencyManagementを使うと、推移的依存として入ってくるartifactのバージョンも管理できます。

ただし、管理側で古いバージョンへ固定すると、上位ライブラリが必要とするAPIがなくなり実行時エラーになる場合があります。バージョンを揃えるだけで互換性が保証されるわけではありません。

dependency:treeで実際の依存を確認する

依存関係の調査には次のコマンドを使います。

mvn dependency:tree

意味は次の通りです。

  • mvn: Mavenを実行するコマンド
  • dependency: Maven Dependency Pluginを指定
  • tree: 依存関係を木構造で表示するgoal

「なぜこのライブラリが入っているのか」「どの経路から違うバージョンが来ているのか」を確認できます。

特定artifactだけ確認する場合は絞り込めます。

mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind

-Dincludes=...は、対象artifactを絞るために渡すプロパティです。

effective-pomで最終設定を見る

親POMやBOMを使っていると、手元のpom.xmlだけでは最終的にどの設定が適用されるか分かりづらくなります。

mvn help:effective-pom
  • help: Maven Help Plugin
  • effective-pom: 継承や管理情報を反映した最終的なPOMを表示するgoal

バージョンを書いていない依存が、どこから値を受け取っているか確認するときに便利です。

BOMをimportする

**BOM(Bill of Materials)**は、関連ライブラリのバージョン管理情報をまとめたPOMです。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>example-bom</artifactId>
      <version>1.2.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

type=pomはJARではなくPOM自体を対象にする指定です。

scope=importは、そのPOMのdependencyManagement情報を現在のPOMへ取り込む指定です。

BOMをimportしても、BOMに載っているライブラリがすべて自動で依存追加されるわけではありません。実際に使うライブラリはdependenciesへ宣言します。

pluginManagementとは別物

dependencyManagementはプロジェクトのライブラリ依存を管理します。

Maven Pluginのバージョンや設定を管理したい場合はpluginManagementを使います。

Maven公式ドキュメントでも、プロジェクトのdependencyManagementはPlugin自身の推移的依存関係には影響しないとされています。

使い分け

設定 役割
dependencies 実際に使う依存ライブラリを追加する
dependencyManagement バージョンやscopeを中央管理する
BOM import 外部POMの管理情報をまとめて取り込む
pluginManagement Maven Pluginを管理する

迷ったときは、「このartifactを実際に使いたいのか」「使う場合のバージョンルールだけ管理したいのか」で判断すると整理しやすいです。

参考