Jigsaw のお勉強 その3
Jigsaw のお勉強の続きです。
超スローペースですがゆっくり慌てず三歩進んで二歩下がるような感じでもう暫く進めていこうと思っています。
今回は、最新Java情報局 「Java SE 9を先取り、Project Jigsawでモジュールを作成する」の写経をおこなってみました。
プログラムは同じではないのですがProject Jigsawで実現される機能の確認は同様にしました。
元記事で解説されているようにモジュールの定義は次のようになっています。
モジュールは複数のパッケージやリソースなどを含む、自己記述可能なコードの集合
今までの JARファイルにモジュール名が付いて依存性や公開範囲を記述できるようになったようです。
詳しくは元記事を参照してくださいませ。
では、モジュールを作っていきます。
プログラムを起動したら挨拶(文字列)を表示させるだけのプログラムです。
挨拶を表示させるためのインタフェースとしてGreetingインタフェース、その実装クラスとしてMultilingualGreetingクラスを作成します。
実装クラスのMultilingualGreetingクラスは日本語の挨拶か英語の挨拶をランダムに返します。
MultilingualGreetingクラスは外部からアクセスされることを避けるようにします。(Java SE 9 からインターフェイスにprivateメソッドが記述できるようになったのは今回は忘れてください。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package jp.yucchi.greeting; import jp.yucchi.greeting.internal.MultilingualGreeting; /** * * @author yucchi */ public interface Greeting { public static Greeting of() { return new MultilingualGreeting(); } public String greeting(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package jp.yucchi.greeting.internal; import java.security.SecureRandom; import jp.yucchi.greeting.Greeting; /** * * @author yucchi */ public class MultilingualGreeting implements Greeting { @Override public String greeting() { return new SecureRandom() .nextInt(2) == 0 ? "こんにちわ ジグソー!" : "Hello Jigsaw!"; } } |
このアプリケーションをモジュールとするためにmodule-info.javaをトップディレクトリに作成します。
1 2 3 4 5 |
module jp.yucchi.greeting { exports jp.yucchi.greeting; } |
exports jp.yucchi.greeting;とmodule-info.javaに記述されています。
これは公開範囲を定義しています。
jp.yucchi.greeting.internalパッケージは公開されてないので外部からアクセスすることはできません。
それではjp.yucchi.greetingモジュールを使用するモジュールを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package jp.yucchi.hello; import jp.yucchi.greeting.Greeting; /** * * @author yucchi */ public class Hello { public static void main(String[] args) { Greeting greeting = Greeting.of(); System.out.println(greeting.greeting()); } } |
続いてこちらもモジュールとするためにmodule-info.javaを作成します。
1 2 3 4 5 6 |
module jp.yucchi.hello { requires jp.yucchi.greeting; exports jp.yucchi.hello; } |
先程作成したjp.yucchi.greetingモジュールを使用するためにrequires jp.yucchi.greeting;と記述しています。
これは依存性の定義となります。
面白いのはexports jp.yucchi.helloと自分自身を公開すると定義しています。
Helloクラスのmain()メソッドをコールすることも外部アクセスとなるからです。
これでシンプルなお試しプログラムが完成しました。
ディレクトリ構造は前回までと少し変更しています。
それぞれコンパイルしてみましょう。
1 2 3 |
javac -d bin/jp.yucchi.greeting src/jp.yucchi.greeting/classes/jp/yucchi/greeting/Greeting.java src/jp.yucchi.greeting/classes/jp/yucchi/greeting/internal/MultilingualGreeting.java src/jp.yucchi.greeting/classes/module-info.java |
1 2 3 |
javac --module-path bin -d bin/jp.yucchi.hello src/jp.yucchi.hello/classes/jp/yucchi/hello/Hello.java src/jp.yucchi.hello/classes/module-info.java |
binディレクトリを作成してコンパイルされたファイルを配置しました。
それでは確認のためにプログラムを実行してみます。
ちゃんとうごきましたね!(^_^)
それではモジュラーJARとしてパッケージ化してみましょう。
1 2 3 4 5 6 7 |
mkdir -p mods jar --create --file=mods/greeting@1.0.jar --module-version=1.0 -C bin/jp.yucchi.greeting . jar --create --file=mods/hello.jar --main-class=jp.yucchi.hello.Hello -C bin/jp.yucchi.hello . |
modsディレクトリを作成してモジュラーJARとしてパッケージングし配置しました。
一応できているみたいですが正しくできているか念の為に確認します。
1 2 3 4 5 |
jar --describe-module --file=mods/greeting@1.0.jar jar --describe-module --file=mods/hello.jar |
モジュラーJARの依存性、公開範囲、メインクラスなどの情報に間違いが無いことを確認することができました。
依存性の定義にjava.baseモジュールがありますがこれはjava.lang.Objectクラスなどが含まれた必要不可欠なもので全てのモジュールに含まれることになります。
greeting@1.0.jarでは非公開パッケージがcontains jp.yucchi.greeting.internalと表示されています。
ここでJava SE 9からjarコマンドが少し変更されているのでヘルプをみておきましょう。
yucchi@ubuntu:~/Documents/MyJigsawProjects/HelloJigsaw$ jar –help
使用方法: jar [OPTION…] [ [–release VERSION] [-C dir] files] …
jarはクラスおよびリソースのアーカイブを作成し、アーカイブから個々のクラスまたは
リソースを操作または復元できます。
例:
# 2つのクラス・ファイルを含むclasses.jarというアーカイブを作成する:
jar –create –file classes.jar Foo.class Bar.class
# foo/のすべてのファイルを含む、既存のマニフェストを使用したアーカイブを作成する:
jar –create –file classes.jar –manifest mymanifest -C foo/ .
# モジュラjarアーカイブを作成する。モジュール・ディスクリプタはclasses/module-info.classに
# ある:
jar –create –file foo.jar –main-class com.foo.Main –module-version 1.0
-C foo/ classes resources
# 既存の非モジュラjarをモジュラjarに更新する:
jar –update –file foo.jar –main-class com.foo.Main –module-version 1.0
-C foo/ module-info.class
# 複数リリースjarを作成し、一部のファイルをMETA-INF/versions/9ディレクトリに配置する:
jar –create –file mr.jar -C foo classes –release 9 -C foo9 classes
jarコマンドを短縮または簡略化するには、個別のテキスト・ファイルで引数を指定し、
記号(@)を接頭辞として使用してjarコマンドに渡します。
例:
# 追加オプションおよびクラス・ファイルのリストをファイルclasses.listから読込みます
jar –create –file my.jar @classes.list
メイン操作モード:
-c、–create アーカイブを作成します
-i,、–generate-index=FILE 指定のjarアーカイブの索引情報を
生成します
-t、–list アーカイブの内容を一覧表示します
-u、–update 既存のjarアーカイブを更新します
-x、–extract 指定の(またはすべての)ファイルをアーカイブから抽出します
-d, –describe-module モジュール・ディスクリプタまたは自動モジュール名を出力します
どのモードでも有効な操作修飾子:
-C DIR 指定のディレクトリに変更し、次のファイルを
取り込みます
-f、–file=FILE アーカイブ・ファイル名。省略した場合、stdinまたは
stdoutのいずれかが操作に基づいて使用されます
–release VERSION 次のすべてのファイルをjarのバージョニングされたディレクトリ
(つまり、META-INF/versions/VERSION/)に配置します
-v、–verbose 標準出力に詳細な出力を生成します
作成または更新モードでのみ有効な操作修飾子:
-e、–main-class=CLASSNAME モジュラまたは実行可能なjarアーカイブに
バンドルされたスタンドアロン・アプリケーションの
アプリケーション・エントリ・ポイント
-m、–manifest=FILE 指定のマニフェスト・ファイルからマニフェスト情報を
取り込みます
-M、–no-manifest エントリのマニフェスト・ファイルを作成しません
–module-version=VERSION モジュラjarの作成時または非モジュラjarの更新時の
モジュール・バージョン
–hash-modules=PATTERN モジュラjarの作成時または非モジュラjarの更新時に
指定のパターンに一致し、直接または間接的に
依存しているモジュールのハッシュを
計算および記録します
-p、–module-path ハッシュを生成するモジュール依存性
の場所
作成、更新および索引生成モードでのみ有効な操作修飾子:
-0, –no-compress 格納のみ。ZIP圧縮を使用しません
その他のオプション:
-h、–help[:compat] これ(オプションで互換性)をhelpに指定します
–help-extra 追加オプションのヘルプを提供します
–version プログラム・バージョンを出力します
モジュール・ディスクリプタ’module-info.class’が指定のディレクトリのルートまたは
jarアーカイブ自体のルートにある場合、アーカイブはモジュラjarです。
次の操作は、モジュラjarの作成時または既存の非モジュラjarの更新時に
のみ有効です: ‘–module-version’、
‘–hash-modules’および’–module-path’。
ロング・オプションへの必須またはオプションの引数は、対応するショート・オプション
に対しても必須またはオプションになります。
Project Jigsawの影響でコマンドが増えていますね。(当たり前か・・・)
最後にこのプログラムを実行してみます。
java --module-path mods --module jp.yucchi.hello
アプリケーション・エントリ・ポイントを設定したモジュールのモジュールパスとモジュール名を指定してjavaコマンドによって実行します。
今回はモジュラーJAR作成時にアプリケーション・エントリ・ポイントを指定してますのでこのように実行できます。
モジュラーJAR作成時にアプリケーション・エントリ・ポイントを設定してない場合は次のように実行します。
1 2 3 |
java --module-path mods --module jp.yucchi.hello/jp.yucchi.hello.Hello |
モジュールのモジュールパスの指定はおなじです。
–module モジュール名/モジュールのメイン・クラス名とアプリケーション・エントリ・ポイント指定しなければいけません。
ついでだから javaコマンドのヘルプも確認しておきます。
yucchi@ubuntu:~/Documents/MyJigsawProjects/HelloJigsaw$ java –help
使用方法: java [options] <mainclass> [args…]
(クラスを実行する場合)
または java [options] -jar <jarfile> [args…]
(jarファイルを実行する場合)
または java [options] -m <module>[/<mainclass>] [args…]
java [options] –module <module>[/<mainclass>] [args…]
(モジュールのメイン・クラスを実行する場合)
メイン・クラス-jar <jarfile>、-mまたは–module
<module>/<mainclass>に続く引数は、メイン・クラスへの引数として渡されます。
オプションは次のとおりです:
-d32 推奨されていません。今後のリリースで削除される予定です
-d64 推奨されていません。今後のリリースで削除される予定です
-cp <ディレクトリおよびzip/jarファイルのクラス検索パス>
-classpath <ディレクトリおよびzip/jarファイルのクラス検索パス>
–class-path <ディレクトリおよびzip/jarファイルのクラス検索パス>
:区切りリスト(ディレクトリ、JARアーカイブ、
ZIPアーカイブ)で、クラス・ファイルの検索用。
-p <module path>
–module-path <module path>…
ディレクトリの:区切りリスト、各ディレクトリ
はモジュールのディレクトリです。
–upgrade-module-path <module path>…
ディレクトリの:区切りリスト、各ディレクトリ
は、ランタイム・イメージ内のアップグレード可能な
モジュールを置換するモジュールのディレクトリです
–add-modules <module name>[,<module name>…]
初期モジュールに加えて解決するルート・モジュール。
<module name>には次も指定できます: ALL-DEFAULT、ALL-SYSTEM、
ALL-MODULE-PATH.
–list-modules
参照可能なモジュールをリストし終了します
-d <module name>
–describe-module <module name>
モジュールを説明し終了します
–dry-run VMを作成しメイン・クラスをロードしますが、メイン・メソッドは実行しません。
–dry-runオプションは、次の検証に役立つ場合があります:
モジュール・システム構成などのコマンド行オプション。
–validate-modules
すべてのモジュールを検証し終了します
–validate-modulesオプションは、次の検索に役立つ場合があります:
モジュール・パス上のモジュールでの競合およびその他のエラー。
-D<name>=<value>
システム・プロパティを設定します
-verbose:[class|module|gc|jni]
詳細出力を有効にします
-version 製品バージョンをエラー・ストリームに出力して終了します
–version 製品バージョンを出力ストリームに出力して終了します
-showversion 製品バージョンをエラー・ストリームに出力して続行します
–show-version
製品バージョンを出力ストリームに出力して続行します
–show-module-resolution
起動時にモジュール解決出力を表示します
-? -h -help
このヘルプ・メッセージをエラー・ストリームに出力します
–help このヘルプ・メッセージを出力ストリームに出力します
-X 追加オプションのヘルプをエラー・ストリームに出力します
–help-extra 追加オプションのヘルプを出力ストリームに出力します
-ea[:<packagename>…|:<classname>]
-enableassertions[:<packagename>…|:<classname>]
指定した粒度でアサーションを有効にします
-da[:<packagename>…|:<classname>]
-disableassertions[:<packagename>…|:<classname>]
指定した粒度でアサーションを無効にします
-esa | -enablesystemassertions
システム・アサーションを有効にします
-dsa | -disablesystemassertions
システム・アサーションを無効にします
-agentlib:<libname>[=<options>]
ネイティブ・エージェント・ライブラリ<libname>をロードします。例: -agentlib:jdwp
-agentlib:jdwp=helpも参照してください
-agentpath:<pathname>[=<options>]
フルパス名を使用して、ネイティブ・エージェント・ライブラリをロードします
-javaagent:<jarpath>[=<options>]
Javaプログラミング言語エージェントをロードします。java.lang.instrumentを参照してください
-splash:<imagepath>
指定されたイメージを含むスプラッシュ画面を表示します
HiDPIスケールのイメージが自動的にサポートされて使用されます
(可能な場合)。スケーリングされないイメージのファイル名(image.extなど)を
引数として-splashオプションに必ず渡す必要があります。
指定された最も適切なスケーリング済イメージが選択されます
(自動的)。
詳細は、SplashScreen APIのドキュメントを参照してください
@argumentファイル
オプションを含む1つ以上の引数ファイル
-disable-@files
さらなる引数ファイル拡張を無効にします
長いオプションの引数を指定する場合、–<name>=<value>または
–<name> <value>を使用できます。
まとめ
今回は何も考えずにほぼ写経しただけなので随分楽に進みました。
しかし、モジュール化のメリットの依存性、公開範囲の制限を定義することを学ぶことができました。(^_^)
module-info.javaにrequiresとexportsを記述して依存と公開の定義をする。
requires モジュール名;とすることで指定したモジュールに依存することを定義します。
今回は使っていませんがrequires transient モジュール名;とすることで推移的依存(依存したモジュールの依存先も依存可能)を定義できます。
また、requires static モジュール名;とすることでコンパイル時のみ依存可能と定義できます。
ちょっと複雑に感じますが慣れてくると便利に依存を定義することができそうですね。
次に公開するパッケージを定義するのにexports パッケージ名;と記述します。
exports パッケージ名 to モジュール名;とすることで特定のモジュールに公開することが可能です。
ゆっくり少しずつだけどお勉強を進めていこう。
TAGS: Java | 2018年1月20日11:05 PM | Comment : 0