GMOアドマーケティングのT.Nです。
今年の9月にJava9が正式にリリースされました。
弊社ではまだJava8を使用していますが、
Java9へのアップグレードに備え、勉強しているところです。
Java9の変更点はいくつかありますが、
最大の変更点とも言えるJigsawについて、以下の本を読んで学んでいます。
本を読んで学んだことなどを、数回に分けてブログに書く予定です。
今回は、Jigsawの概要について書きます。
Jigsawとは
Project Jigsawという、Javaのプラットフォームをモジュールに分割するプロジェクトのことです。
Java9でJDKがモジュールに分割され、
モジュールを使ったアプリケーションの設計が可能になりました。
モジュールとは
他のモジュールとの依存関係や公開範囲が明確な、
適切にカプセル化されたソースコードの集まりです。
モジュール化のメリット
- 外部に公開するもの、公開しないものを明確にできる。
- クラス同士が予期せぬ依存関係を持つことを防ぐことができる。
- 実行時にクラスパスが存在しないことで起きるエラーを防ぐことができる。
- 攻撃にさらされる部分を減らすことができる。(リフレクションによるアクセスも制限できる。)
- 必要なモジュールが明確なので、JVM起動時に必要とするモジュールの最適化ができる。
モジュールの定義
公開するパッケージや依存するモジュールは、module-info.javaに記述します。
module-info.javaは、モジュールごとに定義します。
以下はJava9で追加されたjshellのmodule-info.javaです。
(jshellというのは、コマンドラインでJavaの処理を実行できる機能です。
コマンドラインでjshellと入力することで起動できます。)
module-info.javaには以下のように依存関係が定義されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module jdk.jshell { requires jdk.compiler; requires jdk.internal.ed; requires java.logging; requires jdk.internal.opt; requires transitive java.prefs; requires jdk.internal.le; requires transitive java.compiler; requires transitive jdk.jdi; exports jdk.jshell.spi; exports jdk.jshell.tool; exports jdk.jshell; exports jdk.jshell.execution; uses jdk.internal.editor.spi.BuildInEditorProvider; uses jdk.jshell.spi.ExecutionControlProvider; provides javax.tools.Tool with jdk.internal.jshell.tool.JShellToolProvider; provides jdk.jshell.spi.ExecutionControlProvider with jdk.jshell.execution.JdiExecutionControlProvider, jdk.jshell.execution.LocalExecutionControlProvider, jdk.jshell.execution.FailOverExecutionControlProvider; } |
module-info.javaの記述方法
requires
依存するモジュールを定義できます。
1 2 3 4 5 6 7 8 9 10 |
module jp.gmoam.sample { // 指定したモジュールに依存 requires モジュール名; // 指定したモジュールに依存し、依存されたモジュールからも、指定したモジュールを参照可能にする。 requires transient モジュール名; // コンパイル時のみ依存(実行時には不要) requires static モジュール名; } |
exports
1 2 3 4 5 6 7 |
module jp.gmoam.sample { // パッケージを公開 exports パッケージ名; // パッケージを特定のモジュールに公開 exports パッケージ名 to モジュール名; } |
opens
実行時にリフレクションによる参照を許可するパッケージを定義できます。
opensに指定したパッケージは、コンパイル時には参照することができません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module jp.gmoam.sample { // exportされたクラスのprivateなフィールド、メソッドは、 // リフレクションで参照できないが、 // opensを指定すると、リフレクションが可能になる。 opens パッケージ名; // 特定のモジュールにリフレクションを許可 opens パッケージ名 to モジュール名; } // モジュールにopensをつけることで、 // モジュール全体をリフレクション可能にすることもできる。 opens module jp.gmoam.sample { exports パッケージ名; } |
provides, uses
ServiceLoaderを用いた依存関係を定義できます。
モジュールを利用する側では、
供給側のインターフェースの情報のみを持っていれば、実装クラスの処理を実行することができます。
利用する側には実装クラスの情報がないので、
供給側では実装クラスを柔軟に変更することが可能になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 供給側 module jp.gmoam.sample.api { provides インターフェース名 with 実装クラス名; // それぞれパッケージ名も含めた完全修飾名を指定する。 // インターフェース名と書いているが、 // 抽象クラスや通常のクラスを指定することもできる。 } // 利用側 module jp.gmoam.sample.cli { uses インターフェース名; } |
Annotations
モジュールには、@Deprecatedなどのアノテーションをつけることもできます。
1 2 3 4 5 6 7 8 9 10 11 12 |
// Java9からDeprecatedになり、将来のリリースで使用できなくなる。 @java.lang.Deprecated(since = "9", forRemoval = true) module java.corba { requires java.transaction; requires java.logging; requires transitive java.rmi; requires jdk.unsupported; requires transitive java.desktop; requires java.naming; ....... } |
ClasspathとModule Path
Java9以前では、classpathを参照して、JVMがクラスをロードしていましたが、
Java9からは、module pathが使用されるようになりました。
Java9でもclasspathは存在しますが、モジュールからはclasspathを参照することができません。
モジュール化されていないクラスからは、
classpathを通して今までと同じようにクラスを参照できます。
Automatic ModulesとUnnamed Module
モジュールからは、classpathを参照することができないので、
モジュールからモジュール化されていない既存のJARファイルなどを参照する場合は、
Automatic Modulesという仕組みを使用する必要があります。
コンパイル時にオプションで指定することで、
モジュール化されていないJARファイルなどをモジュールとして扱うことができ、
モジュールから参照することができます。
Automatic Modulesは、モジュールであるため、classpathを参照することができませんが、
classpath上のクラスは、Unnamed Moduleとして扱われ、module pathからも参照できるので、
モジュール化されていないクラスも参照することができます。
Moduleクラスについて
Java9でjava.langにModuleというクラスが追加されました。
Moduleクラスには、addExports、addOpens、addReadsのようなメソッドがあり、
実行時にモジュールを操作して、依存関係を変更することができます。
これらのメソッドを使用して、
本来は公開されるべきではないモジュールに対する参照を得ることなどができそうですが、
実際にはできないようになっています。
これらのメソッドには、
@CallerSensitiveというアノテーションが付与されており、
メソッド内で呼び出し元が適切であるかのチェックが行われています。
変更対象のモジュール内のクラスから実行された場合のみ、変更を行うことができます。
最後に
今回はJava 9 Modularityを読んで学んだことをまとめました。
途中までしか読んでいないので、簡単な内容のみになりましたが、
今後読み進めていく中で学んだことを、弊社でのモジュール化対応に活かし、
次回はそちらの内容についても書けたら良いと考えています。