恥ずかしながら、Mavenのdependencyタグの子要素にoptionalタグというものがあることを最近知ったのでメモ程度にまとめてみようと思います。
公式サイトで説明されているのは、以下のページ。
Introduction – Maven
実際にあるライブラリを元にこのoptionalタグについて確認していきたいと思います。
commons-loggingのバージョン1.1を例に説明したいと思います。このライブラリはとても古いので、今使うのはありえないですが依存関係がシンプルで説明しやすいのでこれをベースに説明します。
まずは、commons-loggingの1.1のpomを見てみます。以下のURLで公開されています。
https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1/commons-logging-1.1.pom
このライブラリのdependencysタグを抜粋すると以下の通りです。見てわかる通り、5つの依存が定義されています。
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>logkit</groupId>
<artifactId>logkit</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
仮にこのcommons-loggingの1.1を、自分のプロジェクトの依存関係モジュールに追加するとどうなるか。
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
追加したプロジェクトで、mvn dependency:treeを実行し依存関係を確認すると以下の通りです。
com.example:sample:jar:1.0-SNAPSHOT
\- commons-logging:commons-logging:jar:1.1:compile
+- log4j:log4j:jar:1.2.12:compile
+- logkit:logkit:jar:1.0.1:compile
+- avalon-framework:avalon-framework:jar:4.1.3:compile
\- javax.servlet:servlet-api:jar:2.3:compile
前段で確認した通り、commons-loggingの1.1のdependencyタグに定義されていたモジュールが推移的に解決されて依存関係として管理されています(junitはscopeがtestなので依存関係は推移的に解決されない)。
しかし、よくよく考えてみるとcommons-loggingといえば汎用ロギングライブラリであり、特定の実装には紐づく必要がないように作られているはずです。
commons-logging自体は、もちろん各種ロギングライブラリを使用するので依存関係としては必要ですが、commons-loggingを使用するプロジェクトはどれか特定のライブラリに紐づくはずで、すべてのライブラリに紐づく必要はありません。
実は、commons-loggingの1.1の次バージョンのcommons-loggingの1.1.1でこの状況が解決しています。事前の説明が長くなりましたが、ここでやっとoptionalタグが出てきます。
commons-loggingの1.1.1のpomは以下の通りです。
https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.pom
dependencysタグを抜粋すると以下の通りです。juit以外のdependencyタグにoptionalタグが追加されています。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>logkit</groupId>
<artifactId>logkit</artifactId>
<version>1.0.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
<version>4.1.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
commons-loggingの1.1の代わりに、commons-loggingの1.1.1を依存関係に追加し、mvn dependency:treeで確認すると以下の通りです。optionalが付与された依存関係がすべて表示されなくなりました。
com.example:sample:jar:1.0-SNAPSHOT
\- commons-logging:commons-logging:jar:1.1.1:compile
このように、optinalタグはそのoptionalタグを付与したモジュールが必要に応じて依存関係として追加されるように制御するための機能です。
今回の例では、例えばcommons-loggingのロギングにlog4jを使いたいといった場合には改めて依存関係をcommons-loggingを使用するユーザーが明示的に定義することになります。そうすることで、不要な依存関係が追加されなくなり、結果としてもモジュールのバージョン間の競合を防ぐことができます。