- Contents -
Javaのモジュールシステム
この記事では、Java9から導入されたモジュール機能。
JPMS (Java Platform Module System)、開発名称Jigsawについて、
カンタンにまとめてみました。
今から勉強するよという方や、
どういうものなのかだけでも、簡単に知っておきたい方など、
入門として参考になると幸いです。
JPMS (Java Platform Module System)
java9 から導入された機能で、パッケージを更に包括したモジュールという概念を追加。
名称: JPMS (Java Platform Module System)
開発段階では、プロジェクト"Jigsaw"と呼ばれていたもの。
java8までは、パッケージのアクセス範囲は。
public, private, package-private, protected といった制御のみでした。
java9では、モジュールという大きい入れモノを用意し、
さらにパッケージを細かくアクセス制御できるようになりました。
それって、一体何が嬉しいの?
モジュール提供者は、
本来アクセスして欲しくないパッケージへのアクセスを、
禁止する事ができるようになります。
publicなクラスなんだけれども、それはライブラリ内だけにとどめたい。
というようなケースに適しています。
実際、すでにあるライブラリは、本来アクセスしてほしくなかったパッケージに、
多くのアプリケーションが依存しており、
スムーズにライブラリ更新ができなくなってしまっているものもある。
publicパッケージの細分化
モジュールで細かく制御できるようになった、publicパッケージ。
- public + exports
指定パッケージを、モジュール外でも参照可能にする。
(今までのpublicと同じイメージ) - public + exports-to
指定パッケージを、特定モジュールに限定して参照可能にする。 - public
自モジュール内部でのみ、publicとする。
後述するモジュール定義ファイルにとくに何も記載しなければ、
内部パッケージのpublicクラスは全てこの状態になります。
module-info.java
モジュールシステムを使う場合は、
新たにモジュール定義ファイルが必要になります。
module-info.java ファイルというのを、javaソースコード直下に配置する。
配下のパッケージは、こういうモジュールですよ。と定義する為のファイルです。
src/main/java/module-info.java
module モジュール名 {
// ここに依存モジュール、公開パッケージを列挙する
}
モジュール名
モジュール名は何でもいいようですが、
パッケージ名由来のものにしておくほうが誤解がないでしょう。
例えば、下記のようなパッケージをまとめるような、モジュールであれば、
net.tyablog.hoge
というモジュール名にしておきます。
- net.tyablog.hoge
- net.tyablog.hoge.piyo
- net.tyablog.hoge.foo.bar
依存モジュール
括弧の中には、requires
を使って、
この定義しているモジュールが、どのモジュールに依存するかを列挙します。
デフォルトで、java.baseというモジュールはロードされています。
java.baseモジュールは、javaのよく使うモジュールがまとめられたもの。
暗黙的に、requires java.base;
されている状態になっています。
書き方
module モジュール名 {
requires 依存モジュール名1;
requires 依存モジュール名2;
requires 依存モジュール名3;
}
transitive で依存関係を伝播
requiresにtransitive記述子を追記すると、依存を伝播させることが可能です。
transitive: 推移的な
単純に、requiresするだけだと、
そのモジュールからしかアクセスできないものになります。
しかし、requires transitive 依存モジュール名;
と書くと、
依存モジュールを伝播させる事ができます。
例
module net.tyablog.sample {
// java.desktopを伝播できる
requires transitive java.desktop;
}
net.tyablog.sample
モジュールを、requiresした場合、
java.desktop
モジュールもreqiresした事と同等になります。
図解
パッケージの公開
モジュールから公開するパッケージは、exports する必要があります。
exports してない、何も書かない場合は、
モジュール内でのみアクセスできる、publicなパッケージとして扱われます。
他のモジュールからアクセスを許可するパッケージは、exports しましょう。
module モジュール名 {
exports パッケージ名1;
exports パッケージ名2;
exports パッケージ名3;
}
モジュールの種類
モジュールの定義方法はわかりましたね。
module-info.java で定義したものは、厳密にはアプリケーションモジュールと呼ばれます。
名称自体には、あまり意味はありません。
後述するモジュールと区別する為に、覚えておくとよいでしょう。
Java でこれから、作るものは積極的にモジュール対応させておくほうが良いです。
しかし、モジュールはJava9から導入されている為、現在、全てのライブラリが、
モジュール対応されているわけでもありません。
対応がされるまでは、新旧が入り混じった状況になります。
そこで、モジュール対応されていないライブラリは、
下記の二種類の形で扱われる事になります。
自動モジュール (Automatic Module)
module-info.java を含まない、jarファイル、
モジュールPATHに配置することで、自動モジュールとして扱われます。
モジュールPATHとは、クラスPATHの代わりになるもので、
クラスPATH同様、jarファイルの場所として、Javaに指定します。
module-info.java が存在しない為、
モジュール名は、マニュフェストファイルの Automatic-Module-Name属性を参照します。
しかし、だいたいはそんな属性を設定はされてないので、
属性値がない場合は、jarファイル名を元に命名されます。
e.g. hoge-1.0.0.jar -> hoge@1
内包するパッケージは、全てexports / opensされたものとして扱われます。
通常アクセス、リフレクションでアクセス可能
無名モジュール (Unnamed Module)
前述の自動モジュールとは違い、jarファイル、classファイルを、
クラスPATHに配置した場合に、無名モジュールとして扱われます。
こちらは無名というだけあって、モジュール名は付与されません。
そして、この状態のモジュールは、自動モジュールからしかアクセスできません。
アプリケーションモジュールからは、アクセスができないので注意。
参照できるようにするには、モジュールパスに配置して、自動モジュールにする。
モジュール種類によるアクセス範囲
モジュールの実行
最後に、
モジュールを使った簡単な、Hello World!
の実装例を載せておきます。
$ tree src
src
└─ main
└─ java
├─ module-info.java
└─ net
└─ tyablog
└─ sample
└─ Sample.java
src/main/java/module-info.java
// モジュールの定義
module net.tyablog.sample {
// 公開するパッケージは、exportsする。
// 外部モジュールを参照する場合は、requiresが追記。
}
src/main/java/net/tyablog/sample/Sample.java
package net.tyablog.sample;
public class Sample {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
コンパイル後、実行する場合は、
クラスPATHの代わりに、モジュールPATHを指定して実行します。
-
-p, --module-path
ビルドしたクラス、jarファイルの場所を指定する。 -
-m, --module
実行するモジュールのメインクラスを指定する。
モジュール名/クラス名
という感じで指定。
ビルドは、Gradleを使っていますが、
./gradlew build
すると以下のように成果物ができます。
$ tree build
├─ classes
│ └─ java
│ └─ main
│ ├─ module-info.class
│ └─ net
│ └─ tyablog
│ └─ sample
│ └─ Sample.class
└─ libs
└─ sample-1.0-SNAPSHOT.jar
ビルドしたクラスファイルを、モジュールPATHに指定して実行。
$ java -p build/classes/java/main -m net.tyablog.sample/net.tyablog.sample.Sample
Hello World!
ビルドしたjarファイルを、モジュールPATHに指定して実行。
java -p build/libs -m net.tyablog.sample/net.tyablog.sample.Sample
Hello World!
まとめ
Java9以降では、JPMSにより、
パッケージを包括する、モジュールという概念が登場。
module-info.java ファイルで、
モジュール名、依存モジュール、公開パッケージを定義できる。
モジュール内のpublicパッケージは、さらに細かくアクセス制御できるようになりました。
- public + exports
requiresしたモジュールは、参照可能になる。 - public + exports-to
指定のモジュールが、requiresした場合にのみ、参照可能。 - public
外部モジュールからは参照不可。
非モジュールのライブラリ(jarファイル)は、以下のように扱われる。
- 自動モジュール (モジュールPATHに配置)
- 無名モジュール (クラスPATHに配置)
無名モジュールは、アプリケーションモジュールからアクセスする事はできない。
実行時は、
クラスPATHの代わりに、モジュールPATH (-p <モジュールPATH>
) を指定して実行する。
実行メインクラスは、-m <モジュール名/メインクラス>
の形で指定する。
1件のコメント