Java SE 11 Silver 学習 モジュール


## はじめに
- [Oracle Certified Java Programmer, Silver SE 11 認定資格](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html)の受験にあたり、Java11について学習／検証した内容を資料としてまとめる。
- 本ドキュメントは、[出題範囲](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html#skill)のうち、以下出題範囲に対応する調査を行った結果となる。
    - モジュール・システム

## 本ドキュメントで解説する内容
- モジュール
    - モジュールの構成
    - モジュールのコンパイル
    - モジュールの実行
    - モジュール情報の確認
    - クラス／モジュールの依存関係確認
    - 非公開パッケージの一時的な公開
- module-info.java

## モジュール
- 「モジュール」とは複数のパッケージ(主にライブラリなど)を管理する単位となる(Java9以降)
- モジュールを利用することで、パッケージ単位でモジュール外への公開／非公開を管理できる。
- モジュール内部でのみ利用しているパッケージが、他のコードからアクセスできないようにする用途などに利用する。
- 標準ライブラリもモジュールとして提供されており、基本的なAPIは`java.base`として全モジュールに提供される。
- モジュール化されていないアプリケーションは、実行時には暗黙的に無名モジュールに属していると見做され、全モジュールを読み込み、全パッケージを公開する。

### モジュールの構成
- srcディレクトリ配下にモジュール用のディレクトリ(下記例だとmymodule)を作成する。
- モジュール用ディレクトリにおいて、`module-info.java`、およびモジュールのソースを含む。
```console
src
└── mymodule
    ├── com
    │   └── sample
    │       ├── A.java
    │       └── B.java
    └── module-info.java
```

### モジュールのコンパイル
- モジュール用のディレクトリに配置した`module-info.java`を他のソースと共にコンパイル(javacコマンド)する。
  ```console
  javac -d [クラスファイルの出力先ディレクトリ] [コンパイルするファイルのパス]

  例:
  $ javac -d src/mods/mymodule src/mymodule/module-info.java src/mymodule/com/sample/*.java

  src
  └── mods
      └── mymodule
          ├── com
          │   └── sample
          │       ├── A.class
          │       └── B.class
          └── module-info.class
  ```
- モジュールのクラスファイルをJARファイルとしてまとめる(jarコマンド)ことも可能。
  ```console
  jar --create --file=[JARファイルのパス] --main-class=[エントリーポイントクラス] -C [クラスファイルが存在するディレクトリ]

  例:
  $ jar --create --file=mlib/mymodule.jar --main-class=com.sample.A -C src/mods/mymodule .

  mlib
  └── mymodule.jar
  ```
- モジュールのクラスファイルをJMODファイルとしてまとめる(jmodコマンド)ことも可能。
  ```console
  jmod create --class-path [クラスファイルが存在するディレクトリ] [JMODファイルのパス]

  例:
  $ jmod create --class-path src/mods/mymodule mlib/mymodule.jmod

  mlib
  └── mymodule.jmod
  ```

### モジュールの実行
- javaコマンドを`--module-path`および`-m`を指定して実行する。
- メインクラスが指定されたJARファイルにアーカイブされたモジュールの場合、クラスは指定不要。

```console
$ java --module-path [モジュールのルートディレクトリ] -m [モジュール名/モジュールのクラス]

例: 
$ java --module-path src/mods -m mymodule/com.sample.A
$ java --module-path mlib -m mymodule
```

### モジュール情報の確認
モジュール情報(`module-info.java`に定義した内容)を確認する方法は以下の2種類となる。
1. javaコマンドを`--module-path`および`--describe-module`を指定して実行する。
    ```console
    $ java --module-path [モジュールのルートディレクトリ] --describe-module [モジュール名]

    例:
    $ java --module-path src/mods --describe-module mymodule
    mymodule file:///opt/src/mods/mymodule/
    requires java.base mandated
    contains com.sample
    ```
2. jmodコマンドを`describe`で実行する。
    ```console
    $ jmod describe [JMODファイルのパス]

    例:
    $ jmod describe mlib/mymodule.jmod
    mymodule
    requires java.base mandated
    contains com.sample
    ```

### クラス／モジュールの依存関係確認
- クラスおよびモジュールの依存関係(`module-info.java`でrequired定義した内容)を確認する方法は以下の2種類となる。
1. クラス／モジュールの依存関係を確認するにはjdepsコマンドを`--list-deps`を指定して実行する。
    ```console
    $ jdeps --list-deps [JARファイルのパス]

    例:
    $ jdeps --list-deps mlib/mymodule.jar
       java.base
    ```
2. モジュールの依存関係を確認するにはjavaコマンドを`--show-module-resolution`で実行する。
    ```console
    $ java --module-path [モジュールのルートディレクトリ] -m [モジュール名/モジュールのクラス] --show-module-resolution

    例:
    $ java --module-path src/mods -m mymodule/com.sample.A --show-module-resolution
    root mymodule file:///opt/src/mods/mymodule/
    java.base binds java.smartcardio jrt:/java.smartcardio
    java.base binds java.naming jrt:/java.naming
    java.base binds jdk.security.jgss jrt:/jdk.security.jgss
    ...
    ```

### 非公開パッケージの一時的な公開
- javacコマンドを`--add-exports`を指定して実行することで、非公開のパッケージを一時的に公開できる。
```console
$ javac  javac -d [クラスファイルの出力先ディレクトリ]
  --module-path [モジュールのルートディレクトリ]
  --add-exports [対象のモジュール/公開するパッケージ=利用するモジュール]
  [コンパイルするファイルのパス]

例:
$ javac -d mods/mymodule
  --module-path mods/ \
  --add-exports mymodule/com.sample=app \  
  src/app/module-info.java src/app/com/Main.java
```

## module-info.java
- モジュールの公開・依存・サービス・リフレクションを定義する。
  ```java
  module mymodule {
    //任意のオペレーションを記載
  }
  ```
- 記載できるのは以下となる。
  - exports系： モジュール外に公開するパッケージを指定する。
  - requires系：　モジュールが利用(依存)する外部モジュールを指定する。
  - uses/provides： サービス(インタフェースの実装)の利用／提供を指定する。
  - open系：　リフレクションの利用を許可する。

|Directive|Sumary|
|--:|--:|
|requires|指定したモジュールをロードする(モジュール依存性)|
|requires static|コンパイル時はモジュールが必要だが、実行時には省略可能(オプション依存性)|
|requires transitive̶|推移的ロード(指定したモジュールが依存するモジュールもロードする)|
|exports|パッケージをエクスポートする(当該パッケージのpublic型に対して、他のモジュールからアクセスできる)|
|exports…to|エクスポートされたパッケージにアクセス可能なモジュール一覧をカンマ区切りで指定する(制限付きエクスポート)|
|uses|モジュールが使用するサービス(インタフェース名／抽象クラス名)を指定する|
|provides…with|サービスの実装(インタフェース定義および実装)を提供する(サービスプロバイダ)
|open|module定義にopenを付加することで、リフレクションによる強制アクセスを許可する(モジュール単位)|
|opens|リフレクションによる強制アクセスを許可する(パッケージ単位)|
|opens…to|リフレクション許可パッケージにアクセス可能なモジュール一覧をカンマ区切りで指定する(制限付きパッケージ単位)|

```java:module-info.java
[open] module <モジュール名> {
  requires <外部モジュール名>; // モジュールのロード(モジュール依存性)
  requires static <外部モジュール名>; // オプション依存性
  requires transitive <外部モジュール名> // モジュールの推移的ロード(モジュール依存性)
  exports <パッケージ名> // 制限なしエクスポート
  exports <パッケージ名> to <外部モジュール名>,<外部モジュール名> // 制限付きエクスポート
  use <インタフェース名／抽象クラス名> // サービスの利用 (サービスコンシューマ)
  provides <インタフェース名／抽象クラス名> with <実装クラス名> // サービスの提供 (サービスプロバイダ)
  opens <パッケージ名> // リフレクションの許可(制限なし)
  opens <パッケージ名> to <外部モジュール名>,<外部モジュール名> // リフレクションの許可(制限付き)
}
```

- 参考: https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-SO17-Modules.pdf