Maven依存関係解決の仕組み

開発・運用ツール

CRAIDでブリッジエンジニアをしているKakeyaです。以前、セブでインターン中にお世話になった現地エンジニアFealroneさんが投稿した記事を翻訳しましたので、当記事で紹介いたします。

こちらが元記事になりますので是非読んでみてください。
翻訳元記事(オフショア子会社Fullspeed Technologies Inc.の技術ブログ)

Mavenの依存関係の仕組み

Mavenの依存関係とは

Mavenの依存メカニズムについて、Apacheから引用:

Dependency management is a core feature of Maven. Managing dependencies for a single project is easy. Managing dependencies for multi-module projects and applications that consist of hundreds of modules is possible. Maven helps a great deal in defining, creating, and maintaining reproducible builds with well-defined classpaths and library versions.

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

つまり、依存関係の管理とは再現性のあるビルドを維持するものであり、これにはバージョンのコンフリクトを解決することも含まれます。このバージョンのコンフリクト解決についてこの記事で詳しく探っていきます。

依存状態における複数バージョンの取り扱い

ほとんどのプロジェクトでは、複雑な依存関係が存在します。Mavenが正しい依存関係のバージョンを維持するためには、推移的依存関係(Transitive Dependency)という概念を利用します。推移的依存関係とは推移性(依存関係A => B、B => CならばA => C)に基づいて依存しているライブラリの依存関係のことを指します。
次のセクションでは、Mavenのアプローチと、この推移的依存関係を解決するのに役立つ機能について探っていきます。

依存関係のコンフリクト解決

Mavenには、複数のバージョンが存在するときにどのアーティファクトのバージョンが選ばれるかを決定するアプローチがあります。
以下の図は、バージョンにコンフリクトがある依存関係の例です。

MavenはPOMファイルからすべての依存関係を推移的に読み取るため、Log4j-1.2.2は Log4j-1.2.3が存在するにもかかわらず、依存関係ツリーに含まれます(そしてバージョンも高い)。Mavenには、このコンフリクトを解決するための戦略があります:

戦略 #1:最も近い定義の使用

Mavenは、ルート(POMファイル)から特定の依存関係までの最も距離の近いアーティファクトのバージョンを使用して依存関係のコンフリクトを解決します。これは、プロジェクトをビルドする際のMavenのデフォルトの戦略です。

コンフリクトする依存関係がプロジェクトの異なるレベルに位置していると仮定します。

Log4jの最も近い定義はLog4j-1.2.3のバージョンであるため、 Log4j-1.2.3がこのプロジェクトで使用されます。そして、2つのバージョンが依存関係ツリーの同じ深さに位置している場合、最初の宣言が選択されます。

戦略 #2:具体的なバージョンの使用

Mavenは、コンフリクトしている依存関係のうちの1つにバージョン指定(各括弧[含む] / 丸括弧[含まない])があると別の戦略に切り替えます。

加筆:バージョンが[1.0,2.0)の場合は1.0以上、2.0未満となり、[1.0,2.0]の場合は1.0以上、2.0以下となります。詳しくはVersion Range Specificationをご確認ください。

例えば、 commons-loggingの下のLog4jが具体的なバージョンを持っている場合、Mavenは自動的にこのバージョンを使用します。

しかし、具体的なバージョンを持つアーティファクトがリポジトリから利用できない場合、エラーが発生し失敗します。

バージョンが次のような範囲にある場合、Mavenは同じ戦略を使用します。
[1.2.2,1.2.5]
Mavenはこの依存関係をどのように解決するでしょう。

最も距離の近い依存性のバージョンが、具体的なバージョン番号の範囲内にある(1.2.3は 1.2.2と 1.2.5の範囲内にある)ため、MavenはLog4j-1.2.3を選択します。

依存関係管理

Mavenでは、プロジェクト作成者は、推移的な依存関係に遭遇した時に、使用するアーティファクトのバージョンを直接指定することができます。また、依存関係ツリー内の他のバージョンを探すことで、バージョンがない依存関係を自動的に解決します。

依存関係スコープ

依存関係の推移性を制限し、依存関係がクラスパスに含まれるタイミングを決定するために使用されます。現在、Mavenには5つのスコープがあります:

加筆:Mavenの依存関係のスコープは、その依存関係がどの範囲で使用されるべきかを定義します。当記事ではスコープは5つと紹介されていますが、Introduction to the Dependency Mechanismではsystemを含めて6つ紹介されています。

compile

何も指定されていない場合のデフォルトのスコープ。このスコープの依存関係は、プロジェクトのすべてのクラスパスに使用されることを意味しています。

provided

アプリケーションサーバー(例:TomcatやWebsphere Application Server)などのホスティング環境の実行時に依存関係を提供することを期待していることを示しています。

runtime

このスコープを持つ依存関係を、アプリケーション実行時およびテストに含めますが、コンパイルには含めません。

加筆:このスコープは、依存関係がコンパイルには必要ないが、実行時には必要であることを示しています。

test

このスコープは、依存関係がアプリケーションの通常の使用には必要なく、テストのコンパイルと実行フェーズでのみ利用可能であることを示しています。このスコープは推移的ではありません。

加筆:testスコープが推移的ではない理由は、テスト用の依存関係が他のプロジェクトやモジュールに影響を与えないようにするためです。

import

このスコープは、<dependencyManagement>セクションのPOMタイプの依存関係でのみサポートされています。これは依存関係が、POMの <dependencyManagement> セクションで指定された依存関係のリストに置き換えられることを示します。そのため、importスコープの依存関係は実際には依存関係の推移性を制限することには関与しません。

加筆:つまり、プロジェクトAがプロジェクトBに依存し、プロジェクトBがimportスコープで他のPOMを参照していても、その依存関係はプロジェクトAには影響しません。

除外された依存関係

Mavenは、 <exclusion> タグを使用して特定の依存関係を明示的に除外することができます。


上記の図では、親commons-loggingが子Log4jに exclusionタグを持っているため、Mavenはビルド中に commons-loggingの Log4jを含めず、代わりに他の Log4jを使用します。

オプションの依存関係

Mavenはデフォルトで依存関係を除外し、明示的に依存関係を含める必要があります。簡単に言えば、オプションの依存関係は「デフォルトで除外されます」。

以下の図を考えてみましょう:

プロジェクトAはプロジェクトBに依存していますが、これは optionalに設定されています。プロジェクトAと依存関係があるプロジェクトXがプロジェクトBを参照しようとすると、 optionalタグがプロジェクトXに推移的に含まれるのを防ぐため、プロジェクトBの参照を取得することはできません。開発者がプロジェクトXでプロジェクトBを参照したい場合、 optionalタグを削除する必要があります。

まとめ

以上のように、Mavenは依存関係を解決するためにさまざまなアプローチを使用します。また、開発者自身に依存関係の推移性に対して対処させる機能も持っています。この記事が、Mavenが依存関係をどのように解決するかについての理解を深めるのに役立つことを願っています。
Mavenに関連する他のトピックについては、こちらのドキュメントから読むことができます。



オフショア開発ならCRAID!

オフショア開発とは、システム開発業務などを海外の開発会社や海外子会社に委託することです。

CRAIDは東証プライム上場のフリービット株式会社の子会社です。CRAIDのオフショア開発拠点「フルスピードテクノロジーズ」は、当初は月間3000億ものリクエスト処理にも対応できる自社システム開発を行うためのオフショア開発部門として始まりました。各グループ会社の開発やクライアント様の受託開発やラボ型開発も多く手掛けております。

CRAIDやオフショア開発に関して、お気軽にお問い合わせください。

この記事を書いた人

エンジニア3年目です。
バックエンドとインフラやってます。

Kakeyaをフォローする
開発・運用ツール開発技術
CRAID オフショア開発ブログ
タイトルとURLをコピーしました