Maven vs Java9のModule(Project Jigsaw) : 依存はどっちが勝つの? (追記:あんまりMaven関係なかった)
無事Moduleを使ったビルドがMavenでできたところで、本当にやりたかった検証へ。
Mavenで複数プロジェクトが同一プロジェクトに依存していると、「どれか一つ」が使われることになります。
全て同じバージョンなら問題にならないのですが、それぞれ異なるバージョンのプロジェクトを使用していると、結果、意図せぬ不具合を引き起こすことになります。
もしかしてmoduleはこの問題も解決したのでは? と期待したのですが… 結果、ダメそうでした!
検証内容
こういう依存関係のものを作ります。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "1"; } }
multi.version/pom.xml(一部)
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>1.0</version>
World.java
package org.astro; import multi.version.MultiVersion; public class World { public static String name() { return "world" + MultiVersion.version(); } }
org.astro/pom.xml (一部)
<dependencies>
<dependency>
<groupId>multiversion</groupId>
<artifactId>multi.version</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
Main.java
package com.greetings; import org.astro.World; public class Main { public static void main(String[]args){ System.out.format("Greetings %s!%n", World.name()); } }
ビルドして実行すると
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world1!
ここまではOK.
さてここで、
multi.versionモジュールのversion2.0を作ります。
依存を追加するだけで、特にrequiresやimportはしません。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "2"; } }
multi.version/pom.xml
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version>
そして com.greetingsのpom.xmlにversion 2.0のmultiversionへのdependencyを追加します。
<dependencies> <dependency> <groupId>groupId</groupId> <artifactId>org.astro</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version> </dependency> </dependencies>
GitHub - taichiw/moduleTry at 3.0_module_tukattemo_dame
これで実行すると…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world2!
2が返ってきてしまいました。
たとえModuleを使っても、Mavenビルド時に、「より外側」に書かれたプロジェクトが使われてしまうことに変わりはないようです。
これだと、Moduleは、Module提供者が意図とした環境での実行が行われるとは限らないことになります。
さらに、MultiVersionクラスのメソッド名を変えてみます。
MultiVersion.java
package multi.version; public class MultiVersion { // changed method name version() -> version3() public static String version3 () { return "3"; } }
これでもビルドは通りますが…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Exception in thread "main" java.lang.NoSuchMethodError: multi.version.MultiVersion.version()Ljava/lang/String; at org.astro.World.name(World.java:6) at com.greetings.Main.main(Main.java:7)
実行時エラーになってしまいました。
残念。
追記 (2017/10/05)
Mavenが悪いみたいな書き方したけど、そもそも現行のModuleの仕様の問題っぽい気がする。
mavenで作られたjarファイルを1ディレクトリに集めて…
$ ls mlib com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar org.astro-1.0-SNAPSHOT.jar
javaコマンドでmoduleとして実行すると
$ java -p mlib -m com.greetings/com.greetings.Main Greetings world1!
multi.versionを2.0に置き換えて実行すると
$ cp multiversion/target/multi.version-2.0.jar mlib/ $ mv mlib/multi.version-1.0.jar mlib/multi.version-1.0.jar_ $ ls mlib/ com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar_ multi.version-2.0.jar org.astro-1.0-SNAPSHOT.jar $ java -p mlib -m com.greetings/com.greetings.Main Greetings world2!
2になっちゃう。
moduleをビルドするときには--module-versionコマンドでバージョン指定しながらモジュールが作れるので、特定のバージョンに対しての依存も貼れそうなんだけど…
ひとまず方法が見つからず。
追記
今日、JavaOneでセッションがあったようで… あとで資料 or tweet or なんか無いか探してみよう。