プロジェクトが依存するライブラリを管理する
Mavenはビルドの際、プロジェクトが依存するライブラリ(ディペンデンシー:Dependency)をリモートリポジトリから自動的にビルド環境にダウンロードしてクラスパスにセットします。この機能のおかげで、入手したMavenプロジェクトを、どこでもビルドできるようになります。
これらの全体メカニズムについては、「Mavenを使った開発」を参照してください。
ここでは開発者やチームで、どのようにして依存ライブラリを管理するのかについて紹介します。
まず最初にディペンデンシーの記述の仕方、そして後半ではチームで開発を行う際のディペンデンシー管理について説明します。
ディペンデンシーの記述の仕方
ディペンデンシーはpom.xmlに記述します。基本書式は以下の通りです。
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
dependenciesタグの中にdependencyタグを使って、いくつでもディペンデンシーを指定できます。
ディペンデンシーとして参照されるライブラリも、Mavenアーティファクトです。したがって、グループID、アーティファクトIDを持っています。そしてバージョン番号を指定することで、ディペンデンシーを特定します。
このサンプルでは、バージョン1.1.3のlog4jを指定しています。
バージョン番号の指定方法には、サンプルのように直接そのバージョン番号を指定する他にもいくつか用意されています。
- SNAPSHOTバージョン
SNAPSHOTバージョンとは、ある目標のバージョンに向けて現在進行形で開発が進むバージョンのことです。
以下の書式で表します。
<バージョン番号>-SNAPSHOT
例えば、"1.0-SNAPSHOT"とはバージョン1.0の開発途中のバージョンになります。
SNAPSHOTバージョンを依存ライブラリに指定した場合、Mavenは通常のバージョン指定とは異なる動きをします。
通常のバージョン指定の場合、一旦ローカルリポジトリに指定されたバージョンのライブラリがダウンロードされると、ずっとそれが参照されます。
一 方、SNAPSHOTは開発が現在進行形で進められているので、その内容は日々アップデートされていきます。Mavenは一旦ローカルリポジトリに取り込 んだ後でも、もしもリモートリポジトリにあるライブラリのファイル更新日付がローカルよりも新しい場合、ローカルのライブラリファイルをリモートのもので 上書きします。
サブプロジェクト単位で複数のチームに分かれて共同作業を行う場合、常に他のチームの最新の開発成果物(Mavenアーティファクト)を自分のプロジェクトに取り込んで参照することができます。
- バージョンレンジ(Version Range)
今度は正式リリースされたライブラリをディペンデンシーに指定する場合を考えます。たとえ正式リリースされたライブラリであったとしても、バグフィックスや機能追加・変更によるバージョンアップは頻繁に行われます。
依存するライブラリのバージョンを自動的に追跡するしくみがバージョンレンジ(Version Range)と呼ばれる機能です。
以下のような書式になります。
[1.0,)
こ の例ではバージョン1.0以上の最新のバージョンに依存するようになります。例えばローカルリポジトリにバージョン1.0が置かれているときに、リモート リポジトリに最新の1.0.1が公開されたとします。すると次回からのビルドでは、1.0.1が自動的にダウンロードされて参照されるようになります。 (※厳密には、settings.xmlのupdatePolicyによって更新タイミングが変わってきます。)
バージョンレンジは一見とても便利に思えます。
し かし自動更新の際、開発者に対して何の確認も行われずにバージョンアップが行われてしまうので、開発者自身そのことに気がつかないこともあります。ある日 突然ビルドがうまくいかなくなった、あるいは開発中のプログラムの動作がおかしくなった・・・そして苦労した挙げ句、結局知らず知らずのうちに行われた依 存ライブラリのバージョンアップが原因だったということにもなりかねません。
バージョンレンジの利用は注意して行うべきでしょう。
ディペンデンシーの指定において、もう一つ重要なキーワードがあります。それはディペンデンシースコープ(Dependency Scope)です。
以下の指定ができます。
- compile
ビルド時、テスト時、ランタイム時、全てのフェーズにおいて参照される依存ライブラリです。
バージョンスコープを何も指定しないときのデフォルト値になります。
- provided
compileスコープと同じ働きをしますが、JVMやコンテナのクラスローダによって提供されるライブラリです。
例えば、WARアーティファクトを開発するプロジェクトでデフォルトのcompileスコープを指定した場合、その依存ライブラリはWARファイルに同梱(WEB-INF/libの下)されるのですが、providedスコープを指定すると同梱されません。
- runtime
ビルド時には参照されずに、ランタイム時にのみ参照されます。
- test
テストプログラムのビルド、およびテストの実行時にのみ参照されます。
以下のように使います。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
この例では、テストプログラムのコンパイル、およびテストの実行時にのみjunitを使うことを定義しています。
チームでの依存ライブラリの管理
複数のサブプロジェクトに分かれて行うようなチーム開発において、それぞれのサブプロジェクトで同じディペンデンシーを参照することがあると思います。このようなとき、各サブプロジェクト間で利用するディペンデンシーのバージョンは合わせておきたいものです。あ るいは、組織の中で共通ライブラリや共通コンポーネントを開発して提供している場合があると思います。これらの共通ライブラリや共通コンポーネントもバグ による修正や、新たな要求によるバージョンアップとは無縁ではありません。その際、よっぽど組織内の情報通達の徹底が行われていないと、バージョンアップ したのはいいものの、昔のままの開発環境で開発を行って、いざ本番環境でのシステムテストのフェーズになって、うまく動かない・・・ということも起こるか もしれません。
バージョンレンジを使うことによって、全プロジェクトチームが使用する共通ライブラリや共通コンポーネントのバージョンを常に最新に保つ方法もあります。でもやっぱりバージョンアップするかどうかの判断には、開発者の意志を込めたいものです。
このような場合、「はじめの第一歩」でも紹介した親POMを使って、プロジェクトで共通して使用するライブラリ(ディペンデンシー)を管理することができます。
親POMにプロジェクト全体、あるいは所属している組織全体で共通して利用するライブラリやコンポーネントを、ディペンデンシーとして記述しておきます。各プロジェクトのPOMからこれを継承することによって、同じライブラリを利用することができるようになります。
依 存ライブラリのバージョンアップですが、これは共通POMの管理者(開発環境担当者、ライブラリアン、プロジェクトリーダーなどのロールをもった人)の意 志で判断します。バージョンアップが必要だという判断になれば、共通POMを書き換えて共通POMのバージョンアップを行います。そして各プロジェクトで このバージョンアップした親POMを利用するように指示します。
親POMへの依存ライブラリの書き方には、2通りの方法があります。
- dependencies
通常のdependenciesタグを使って定義された依存ライブラリは、その親POMを継承するプロジェクトからも無条件で参照されるようになります。このとき、継承する子POMに依存ライブラリについて何も記述する必要はありません。
【親POMの例】
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
【子POMの例】
<parent>
<groupId>com.mycompany.common</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0</version>
</parent>
親POMを継承するだけで、ディペンデンシーの指定は必要ありません。
- dependencyManagement
dependencyManagementタグを使って定義された依存ライブラリを、その親POMを継承するプロジェクトから利用する場合、子POM内で明示的にその依存ライブラリを使うことを宣言しなければなりません。
このとき親POM内で定義されたバージョン情報が継承されるようになります。結果、親POMから依存ライブラリのバージョンをコントロールすることが可能となります。
【親POMの例】
<dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
</dependencyManagement>
【子POMの例】
<parent>
<groupId>com.mycompany.common</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0</version>
</parent>
・・・省略・・・
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
子POMに明示的にディペンデンシーを宣言します。宣言しない場合、この子プロジェクト内でlog4jを参照することはありません。またバージョン番号が指定されていないことに注意してください。
必ずしも全てのプロジェクトで利用されるわけではないけれども、利用する場合のバージョンは管理しておきたい・・・このような場合にはdependencyManagementタグを利用するといいでしょう。