1前書き

次のリンク:/java-9-modularity[Java 9 Modularityのガイド]この記事では、Java Platform Module Systemと同時に導入された

java.lang.Module

APIについて説明します。

このAPIはプログラム的にモジュールにアクセスする方法、モジュールから特定の情報を検索する方法、そして一般的にそれとその

ModuleDescriptor

を扱う方法を提供します。


2モジュール情報の読み方


Module

クラスは、名前付きモジュールと名前なしモジュールの両方を表します。

名前付きモジュールは名前を持ち、モジュールレイヤを作成するときにJava仮想マシンによって構築されます。

定義としてモジュールのグラフを使用します。

名前のないモジュールには名前がなく、__ClassLoaderごとに名前があります。** 名前付きモジュールにない型はすべて、クラスローダーに関連する名前のないモジュールのメンバーです。


Module

クラスの興味深い部分は、モジュール名、モジュールクラスローダー、モジュール内のパッケージなど、モジュールから情報を取得できるようにするメソッドを公開することです。

モジュールが名前付きか名前なしかを調べることがどのように可能であるかを見てみましょう。


2.1. 名前付きまたは名前なし


isNamed()

メソッドを使用して、モジュールに名前が付けられているかどうかを識別できます。


HashMap

などの特定のクラスが名前付きモジュールの一部であるかどうか、またその名前を取得する方法を確認する方法を見てみましょう。

Class<HashMap> hashMapClass = HashMap.class;
Module javaBaseModule = hashMapClass.getModule();

assertThat(javaBaseModule.isNamed(), is(true));
assertThat(javaBaseModule.getName(), is("java.base"));

それでは、

Person

クラスを定義しましょう。

public class Person {
    private String name;

   //constructor, getters and setters
}

同様に、

HashMap

クラスで行ったように、

Person

クラスが名前付きモジュールの一部であるかどうかを確認できます。

Class<Person> personClass = Person.class;
Module module = personClass.getModule();

assertThat(module.isNamed(), is(false));
assertThat(module.getName(), is(nullValue()));


2.2. パッケージ

モジュールを扱うときは、モジュール内でどのパッケージが利用可能かを知ることが重要かもしれません。


java.lang.annotation

などの特定のパッケージが特定のモジュールに含まれているかどうかを確認する方法を見てみましょう。

assertTrue(javaBaseModule.getPackages().contains("java.lang.annotation"));
assertFalse(javaBaseModule.getPackages().contains("java.sql"));


2.3. 注釈

同様に、パッケージに関しても、

getAnnotations()

メソッドを使用して** モジュール内に存在するアノテーションを取得することが可能です。

名前付きモジュールに注釈が存在しない場合、メソッドは空の配列を返します。


java.base

モジュールに存在するアノテーションの数を見てみましょう。

assertThat(javaBaseModule.getAnnotations().length, is(0));

名前のないモジュールで呼び出されると、

getAnnotations()

メソッドは空の配列を返します。


2.4. ClassLoader


Module

クラス内で使用可能な

getClassLoader()

メソッドのおかげで、与えられたモジュールの

ClassLoader

を取得することができます。

assertThat(
  module.getClassLoader().getClass().getName(),
  is("jdk.internal.loader.ClassLoaders$AppClassLoader")
);


2.5. 層

モジュールから抽出できるもう1つの貴重な情報は

ModuleLayer

です。これは、Java仮想マシンのモジュールの層を表します。

  • モジュール層は、モジュールからロードされる可能性のあるクラスについてJVMに通知します。このようにして、JVMは各クラスがどのモジュールのメンバーであるかを正確に認識します。


ModuleLayer

には、その設定、親レイヤ、およびそのレイヤ内で使用可能な一連のモジュールに関する情報が含まれています。

与えられたモジュールの

ModuleLayer

を取得する方法を見てみましょう:

ModuleLayer javaBaseModuleLayer = javaBaseModule.getLayer();


ModuleLayer

を取得したら、その情報にアクセスできます。

assertTrue(javaBaseModuleLayer.configuration().findModule("jaa.base").isPresent());
assertThat(javaBaseModuleLayer.configuration().modules().size(), is(78));

特別なケースとして、Java Virtual Machineの起動時に作成されるブート層があります。ブート層は、

java.base

モジュールを含む唯一の層です。


3

ModuleDescriptor


を扱う


ModuleDescriptor

は、名前付きモジュールを記述し、その各コンポーネントを取得するメソッドを定義します。


ModuleDescriptor

オブジェクトは不変であり、複数の並行スレッドによる使用に対して安全です。

__ModuleDescriptorを取得する方法を見てみましょう。


3.1.

ModuleDescriptor


を取得する


ModuleDescriptor



Module

と密接に関係しているため、__Moduleから直接取得することができます。

ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();


3.2.

ModuleDescriptor


を作成する


  • ModuleDescriptor.Builder

    クラスを使用して、またはバイナリ形式のモジュール宣言の

    module-info.class

    を読み取ることによって、モジュール記述子を作成することもできます。


ModuleDescriptor.Builder

APIを使用してモジュール記述子を作成する方法を見てみましょう。

ModuleDescriptor.Builder moduleBuilder = ModuleDescriptor
  .newModule("baeldung.base");

ModuleDescriptor moduleDescriptor = moduleBuilder.build();

assertThat(moduleDescriptor.name(), is("baeldung.base"));

これで通常のモジュールを作成しましたが、オープンモジュールまたは自動モジュールを作成する場合は、それぞれ

newOpenModule()

メソッドまたは

newAutomaticModule()

メソッドを使用できます。


3.3. モジュールの分類

モジュール記述子は、通常、オープン、または自動モジュールを記述します。


ModuleDescriptor

内で利用可能なメソッドのおかげで、モジュールのタイプを識別することが可能です:

ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();

assertFalse(moduleDescriptor.isAutomatic());
assertFalse(moduleDescriptor.isOpen());


3.4. 必要なものを取得する

モジュール記述子を使うと、モジュールの依存関係を表す

Requires

のセットを取得することができます。

これは

requires()

メソッドを使用して可能です。

Set<Requires> javaBaseRequires = javaBaseModule.getDescriptor().requires();
Set<Requires> javaSqlRequires = javaSqlModule.getDescriptor().requires();

Set<String> javaSqlRequiresNames = javaSqlRequires.stream()
  .map(Requires::name)
  .collect(Collectors.toSet());

assertThat(javaBaseRequires, empty());
assertThat(javaSqlRequires.size(), is(3));
assertThat(
  javaSqlRequiresNames,
  containsInAnyOrder("java.base", "java.xml", "java.logging")
);


  • java





    base

    を除くすべてのモジュールは、依存関係として

    java





    base

    モジュールを持ちます。

ただし、モジュールが自動モジュールの場合は、

java.base

を除いて一連の依存関係は空になります。


3.5. 提供元を取得する

__provides()メソッドを使用すると、モジュールが提供するサービスのリストを取得することができます。

Set<Provides> javaBaseProvides = javaBaseModule.getDescriptor().provides();
Set<Provides> javaSqlProvides = javaSqlModule.getDescriptor().provides();

Set<String> javaBaseProvidesService = javaBaseProvides.stream()
  .map(Provides::service)
  .collect(Collectors.toSet());

assertThat(
  javaBaseProvidesService,
  contains("java.nio.file.spi.FileSystemProvider")
);
assertThat(javaSqlProvides, empty());


3.6. エクスポートを取得する


exports()

メソッドを使用すると、モジュールがパッケージをエクスポートしているかどうか、特に次のことがわかります。

Set<Exports> javaBaseExports = javaBaseModule.getDescriptor().exports();
Set<Exports> javaSqlExports = javaSqlModule.getDescriptor().exports();

Set<String> javaSqlExportsSource = javaSqlExports.stream()
  .map(Exports::source)
  .collect(Collectors.toSet());

assertThat(javaBaseExports.size(), is(108));
assertThat(javaSqlExports.size(), is(3));
assertThat(
  javaSqlExportsSource,
  containsInAnyOrder("java.sql","javax.transaction.xa", "javax.sql")
);

特別な場合として、モジュールが自動モジュールの場合、エクスポートされたパッケージのセットは空になります。


3.7. 用途の検索


uses()

メソッドを使用すると、モジュールの一連のサービス依存関係を取得することができます。

Set<String> javaBaseUses = javaBaseModule.getDescriptor().uses();
Set<String> javaSqlUses = javaSqlModule.getDescriptor().uses();

assertThat(javaBaseUses.size(), is(34));
assertThat(javaSqlUses, contains("java.sql.Driver"));

モジュールが自動モジュールの場合、依存関係のセットは空になります。


3.8. オープンを取得中

モジュールの開いているパッケージのリストを取得したいときはいつでも、

opens()

メソッドを使うことができます。

Set<Opens> javaBaseUses = javaBaseModule.getDescriptor().opens();
Set<Opens> javaSqlUses = javaSqlModule.getDescriptor().opens();

assertThat(javaBaseUses, empty());
assertThat(javaSqlUses, empty());

モジュールがオープンまたは自動の場合、セットは空になります。


4モジュールを扱う

モジュールから情報を読むこと以外に

Module

APIを使って作業して、モジュール定義を更新することができます。


4.1. エクスポートを追加する

特定のモジュールから特定のパッケージをエクスポートして、モジュールを更新する方法を見てみましょう。

Module updatedModule = module.addExports(
  "com.baeldung.java9.modules", javaSqlModule);

assertTrue(updatedModule.isExported("com.baeldung.java9.modules"));

これは、呼び出し側のモジュールがコードがメンバーとなっているモジュールである場合にのみ可能です。

補足として、パッケージがモジュールによってすでにエクスポートされている場合、またはモジュールが開いているパッケージである場合、影響はありません。


4.2. 読み取りを追加する

与えられたモジュールを読み込むためにモジュールを更新したいときは、

addReads()

メソッドを使うことができます。

Module updatedModule = module.addReads(javaSqlModule);

assertTrue(updatedModule.canRead(javaSqlModule));

このメソッドは、すべてのモジュールが自分自身を読み取るので、モジュール自体を追加しても何もしません。

同様に、このメソッドは、モジュールが名前のないモジュールである場合、またはこのモジュールが既に他のモジュールを読み込んでいる場合には、何もしません。


4.3. オープンを追加する

パッケージを開いたモジュールを少なくとも呼び出し元のモジュールに更新したい場合は、

addOpens()

を使用してパッケージを別のモジュールに開くことができます。

Module updatedModule = module.addOpens(
  "com.baeldung.java9.modules", javaSqlModule);

assertTrue(updatedModule.isOpen("com.baeldung.java9.modules", javaSqlModule));

指定されたモジュールに対してパッケージがすでに開かれている場合、このメソッドは効果がありません。


4.4. 用途を追加する

サービスの依存関係を追加するモジュールを更新したいときはいつでも、メソッド

addUses()

が私たちの選択です:

Module updatedModule = module.addUses(Driver.class);

assertTrue(updatedModule.canUse(Driver.class));

このメソッドは、名前のないモジュールまたは自動モジュールで呼び出されても何もしません。


5結論

この記事では、モジュールの情報を取得する方法、モジュールに関する追加情報にアクセスするための

ModuleDescriptor

の使用方法、およびその操作方法を学習した

java.lang.Module

APIの使用方法を調べました。

いつものように、この記事のすべてのコード例はhttps://github.com/eugenp/tutorials/tree/master/core-java-9[GitHub上で動く]にあります。