Jigsaw のお勉強 その1
Java SE 9 がリリースされて随分経ちますが Jigsaw は積極的に使ってないのでさっぱり解らないのが現状です。
リリースされる前から注目されていてネット上には記事がちらほら見受けられていました。
それを読んではいたのですが未だに自分でコードを書いたことは無いのでそろそろなんとかしなければいけないと思い、
冷やし中華はじめました!
じゃなくて、Jigsaw はじめました!
とりあえず何から手を付けて良いものか解らないので Project Jigsaw: Module System Quick-Start Guide を読んでみることにしました。
Project Jigsaw: Module System Quick-Start Guide
このドキュメントでは、開発者がモジュールを使い始めるための簡単な例をいくつか紹介します。
サンプルコードのファイルパスはスラッシュを使用し、パス区切り子はコロンです。
Microsoft Windowsの開発者は、ファイルパスにバックスラッシュとセミコロンをパス区切り文字として使用する必要があります。
- Greetings
- Greetings world
- Multi-module compilation
- Packaging
- Missing requires or missing exports
- Services
- The linker
- –patch-module
Greetings
この最初の例はcom.greetingsという名前のモジュールで、単に “Greetings!”を表示します。
モジュールは、モジュール宣言(module-info.java)とメインクラスの2つのソースファイルで構成されています。
慣例により、モジュールのソースコードは、モジュールの名前であるディレクトリにあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
src/com.greetings/com/greetings/Main.java src/com.greetings/module-info.java $ cat src/com.greetings/module-info.java module com.greetings { } $ cat src/com.greetings/com/greetings/Main.java package com.greetings; public class Main { public static void main(String[] args) { System.out.println("Greetings!"); } } |
ソースコードは、次のコマンドでディレクトリmods / com.greetingsにコンパイルされます。
1 2 3 4 5 |
$ mkdir -p mods/com.greetings $ javac -d mods/com.greetings \ src/com.greetings/module-info.java \ src/com.greetings/com/greetings/Main.java |
これで、次のコマンドでこの例を実行します。
1 |
$ java --module-path mods -m com.greetings/com.greetings.Main |
–module-pathはモジュールパスです。その値はモジュールを含む1つ以上のディレクトリです。 -mオプションは、メインモジュールを指定します。スラッシュの後の値は、モジュール内のメインクラスのクラス名です。
Greetings world
この2番目の例は、モジュール宣言を更新して、org.astroモジュールへの依存関係を宣言します。
モジュールorg.astroはAPIパッケージorg.astroをエクスポートします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
src/org.astro/module-info.java src/org.astro/org/astro/World.java src/com.greetings/com/greetings/Main.java src/com.greetings/module-info.java $ cat src/org.astro/module-info.java module org.astro { exports org.astro; } $ cat src/org.astro/org/astro/World.java package org.astro; public class World { public static String name() { return "world"; } } $ cat src/com.greetings/module-info.java module com.greetings { requires org.astro; } $ cat src/com.greetings/com/greetings/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()); } } |
モジュールは一度に1つずつコンパイルされます。
モジュールcom.greetingsをコンパイルするjavacコマンドは、モジュールorg.astroへの参照と、エクスポートされたパッケージの型を解決できるように、モジュールパスを指定します。
1 2 3 4 5 6 7 |
$ mkdir -p mods/org.astro mods/com.greetings $ javac -d mods/org.astro \ src/org.astro/module-info.java src/org.astro/org/astro/World.java $ javac --module-path mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java |
この例は、最初の例とまったく同じ方法で実行されます。
1 2 |
$ java --module-path mods -m com.greetings/com.greetings.Main Greetings world! |
Multi-module compilation
前の例では、モジュールcom.greetingsとモジュールorg.astroが別々にコンパイルされていました。 1つのjavacコマンドで複数のモジュールをコンパイルすることもできます。
1 2 3 4 5 6 7 8 9 |
$ mkdir mods $ javac -d mods --module-source-path src $(find src -name "*.java") $ find mods -type f mods/com.greetings/com/greetings/Main.class mods/com.greetings/module-info.class mods/org.astro/module-info.class mods/org.astro/org/astro/World.class |
Packaging
これまでの例では、コンパイルされたモジュールの内容がファイルシステム上で展開されています。
移植と配備の目的では、通常、モジュールをモジュラーJARとしてパッケージ化する方が便利です。
モジュラーJARは、トップレベルのディレクトリにmodule-info.classを持つ通常のJARファイルです。
次の例では、org.astro@1.0.jarおよびcom.greetings.jarをmlibディレクトリに作成します。
1 2 3 4 5 6 7 8 9 10 |
$ mkdir mlib $ jar --create --file=mlib/org.astro@1.0.jar \ --module-version=1.0 -C mods/org.astro . $ jar --create --file=mlib/com.greetings.jar \ --main-class=com.greetings.Main -C mods/com.greetings . $ ls mlib com.greetings.jar org.astro@1.0.jar |
この例では、モジュールorg.astroは、そのバージョンが1.0であることを示すようにパッケージ化されています。 モジュールcom.greetingsは、メインクラスがcom.greetings.Mainであることを示すようにパッケージ化されています。 メインクラスを指定することなく、モジュールcom.greetingsを実行できるようになりました。
1 2 |
$ java -p mlib -m com.greetings Greetings world! |
–module-pathの代わりに-pを使用することによって、コマンドラインも短縮されます。
jarツールには多くの新しいオプションがあります(jar -helpを参照)。その1つはモジュラーJARとしてパッケージ化されたモジュールのモジュール宣言を出力することです。
1 2 3 4 |
$ jar --describe-module --file=mlib/org.astro@1.0.jar org.astro@1.0 jar:file:///d/mlib/org.astro@1.0.jar/!module-info.class exports org.astro requires java.base mandated |
Missing requires or missing exports
ここで、前の例で、com.greetingsモジュール宣言からの要求を誤って省略した場合の動作を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ cat src/com.greetings/module-info.java module com.greetings { // requires org.astro; } $ javac --module-path mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java src/com.greetings/com/greetings/Main.java:2: error: package org.astro is not visible import org.astro.World; ^ (package org.astro is declared in module org.astro, but module com.greetings does not read it) 1 error |
このモジュール宣言を修正しましたが、別の間違いを導入しました。
今回はorg.astroモジュール宣言からのエクスポートを省略します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ cat src/com.greetings/module-info.java module com.greetings { requires org.astro; } $ cat src/org.astro/module-info.java module org.astro { // exports org.astro; } $ javac --module-path mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java $ javac --module-path mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java src/com.greetings/com/greetings/Main.java:2: error: package org.astro is not visible import org.astro.World; ^ (package org.astro is declared in module org.astro, which does not export it) 1 error |
Services
サービスは、サービスコンシューマモジュールとサービスプロバイダモジュールとの間の疎結合を可能にする。
この例には、サービスコンシューマモジュールとサービスプロバイダモジュールがあります。
モジュールcom.socketは、ネットワークソケット用のAPIをエクスポートします。
このパッケージがエクスポートされるように、APIはcom.socketパッケージ内にあります。
APIは、代替実装を可能にするためにプラガブルです。
サービスタイプは同じモジュール内のクラスcom.socket.spi.NetworkSocketProviderであるため、パッケージcom.socket.spiもエクスポートされます。
モジュールorg.fastsocketはサービスプロバイダモジュールです。
com.socket.spi.NetworkSocketProviderの実装を提供します。
パッケージをエクスポートしません。
以下はcom.socketモジュールのソースコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
$ cat src/com.socket/module-info.java module com.socket { exports com.socket; exports com.socket.spi; uses com.socket.spi.NetworkSocketProvider; } $ cat src/com.socket/com/socket/NetworkSocket.java package com.socket; import java.io.Closeable; import java.util.Iterator; import java.util.ServiceLoader; import com.socket.spi.NetworkSocketProvider; public abstract class NetworkSocket implements Closeable { protected NetworkSocket() { } public static NetworkSocket open() { ServiceLoader<NetworkSocketProvider> sl = ServiceLoader.load(NetworkSocketProvider.class); Iterator<NetworkSocketProvider> iter = sl.iterator(); if (!iter.hasNext()) throw new RuntimeException("No service providers found!"); NetworkSocketProvider provider = iter.next(); return provider.openNetworkSocket(); } } $ cat src/com.socket/com/socket/spi/NetworkSocketProvider.java package com.socket.spi; import com.socket.NetworkSocket; public abstract class NetworkSocketProvider { protected NetworkSocketProvider() { } public abstract NetworkSocket openNetworkSocket(); } |
以下はorg.fastsocketモジュールのソースコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
$ cat src/org.fastsocket/module-info.java module org.fastsocket { requires com.socket; provides com.socket.spi.NetworkSocketProvider with org.fastsocket.FastNetworkSocketProvider; } $ cat src/org.fastsocket/org/fastsocket/FastNetworkSocketProvider.java package org.fastsocket; import com.socket.NetworkSocket; import com.socket.spi.NetworkSocketProvider; public class FastNetworkSocketProvider extends NetworkSocketProvider { public FastNetworkSocketProvider() { } @Override public NetworkSocket openNetworkSocket() { return new FastNetworkSocket(); } } $ cat src/org.fastsocket/org/fastsocket/FastNetworkSocket.java package org.fastsocket; import com.socket.NetworkSocket; class FastNetworkSocket extends NetworkSocket { FastNetworkSocket() { } public void close() { } } |
簡単にするために、両方のモジュールをまとめてコンパイルします。 実際には、サービスコンシューマモジュールとサービスプロバイダモジュールは、ほぼ常に別々にコンパイルされます。
1 2 |
$ mkdir mods $ javac -d mods --module-source-path src $(find src -name "*.java") |
最後に、モジュールcom.greetingsを変更してAPIを使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ cat src/com.greetings/module-info.java module com.greetings { requires com.socket; } $ cat src/com.greetings/com/greetings/Main.java package com.greetings; import com.socket.NetworkSocket; public class Main { public static void main(String[] args) { NetworkSocket s = NetworkSocket.open(); System.out.println(s.getClass()); } } $ javac -d mods/com.greetings/ -p mods $(find src/com.greetings/ -name "*.java") |
最後に実行します。
1 2 |
$ java -p mods -m com.greetings/com.greetings.Main class org.fastsocket.FastNetworkSocket |
出力は、サービスプロバイダが見つかったこと、およびNetworkSocketのファクトリとして使用されたことを確認します。
The linker
jlinkはリンカーツールであり、モジュールのセットとそれらの推移的依存とをリンクしてカスタムモジュラーランタイムイメージを作成するために使用することができます(JEP 220を参照)。
このツールでは、現在、モジュールパス上のモジュールをモジュラーJARまたはJMOD形式でパッケージ化する必要があります。
JDKビルドでは、標準およびJDK固有のモジュールをJMOD形式でパッケージ化します。
次の例では、モジュールcom.greetingsとその推移依存を含むランタイムイメージを作成します。
1 |
jlink --module-path $JAVA_HOME/jmods:mlib --add-modules com.greetings --output greetingsapp |
–module-pathの値は、パッケージ化されたモジュールを含むディレクトリのPATHです。Microsoft Windowsでは パスセパレータ ‘:’を ‘;’に置き換えます。
$ JAVA_HOME / jmodsは、java.base.jmodとその他の標準モジュールとJDKモジュールを含むディレクトリです。
モジュールパス上のディレクトリmlibには、モジュールcom.greetingsの成果物が含まれています。
jlinkツールは、生成されたイメージをカスタマイズするための多くの高度なオプションをサポートしています。詳細はjlink –helpを参照してください。
--patch-module
Doug LeaのCVSからjava.util.concurrentクラスをチェックアウトする開発者は、ソースファイルをコンパイルし、それらのクラスを-Xbootclasspath / pでデプロイするために使用されます。
-Xbootclasspath / pが削除された場合、そのモジュールの置き換えは、モジュール内のクラスを上書きするオプション–patch-moduleです。 また、モジュールの内容を増やすために使用することもできます。 –patch-moduleオプションはjavacでもサポートされており、モジュールのようにコードをコンパイルします。
以下は、新しいバージョンのjava.util.concurrent.ConcurrentHashMapをコンパイルし、それを実行時に使用する例です。
1 2 3 4 |
javac --patch-module java.base=src -d mypatches/java.base \ src/java.base/java/util/concurrent/ConcurrentHashMap.java java --patch-module java.base=mypatches/java.base ... |
More information
The State of the Module System
Feedback
Please send usage questions and experience reports to the jigsaw-dev list. Specific suggestions about the design of the module system should be sent to the JSR 376 Expert Group’s comments list.
機械翻訳によって日本語化してみました。
この資料が古くてカビが生えてなければいいのですが・・・ とりあえず読んでみました。
TAGS: Java | 2018年1月4日7:50 PM | Comments : 4