Java 9 ModularityでJigsawを学ぶ

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には以下のように依存関係が定義されています。

module-info.javaの記述方法

requires

依存するモジュールを定義できます。

exports

公開するパッケージを定義できます。
(privateなフィールド、メソッドなどのリフレクションは許可しません。)

opens

実行時にリフレクションによる参照を許可するパッケージを定義できます。
opensに指定したパッケージは、コンパイル時には参照することができません。

provides, uses

ServiceLoaderを用いた依存関係を定義できます。
モジュールを利用する側では、
供給側のインターフェースの情報のみを持っていれば、実装クラスの処理を実行することができます。
利用する側には実装クラスの情報がないので、
供給側では実装クラスを柔軟に変更することが可能になります。

Annotations

モジュールには、@Deprecatedなどのアノテーションをつけることもできます。

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を読んで学んだことをまとめました。
途中までしか読んでいないので、簡単な内容のみになりましたが、
今後読み進めていく中で学んだことを、弊社でのモジュール化対応に活かし、
次回はそちらの内容についても書けたら良いと考えています。