1. 序章

Java 9モジュール性のガイドに続いて、この記事では、Javaプラットフォームと一緒に導入された java .lang.ModuleAPIについて説明します。モジュールシステム。

このAPIは、プログラムでモジュールにアクセスし、モジュールから特定の情報を取得し、通常はモジュールとその Module Descriptorを操作する方法を提供します。

2. モジュール情報の読み取り

Module クラスは、名前付きモジュールと名前なしモジュールの両方を表します。 名前付きモジュールには名前があり、モジュールのグラフを定義として使用して、モジュール層を作成するときにJava仮想マシンによって構築されます

名前のないモジュールには名前がなく、ClassLoaderごとに1つあります。名前の付いたモジュールにないすべてのタイプは、クラスローダーに関連する名前のないモジュールのメンバーです。

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

モジュールに名前が付けられているか、名前が付けられていないかを確認する方法を見てみましょう。

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

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

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

Module javaBaseModule = HashMap.class.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クラスが名前付きモジュールの一部であるかどうかを確認できます。

Module module = Person.class.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("java.base").isPresent());

特殊なケースは、Java仮想マシンの起動時に作成されるブートレイヤーです。 ブートレイヤーは、java.baseモジュールを含む唯一のレイヤーです。

3. ModuleDescriptorの処理

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

ModuleDescriptor オブジェクトは不変であり、複数の同時スレッドで安全に使用できます。

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

3.1. ModuleDescriptorを取得しています

ModuleDescriptorModuleに緊密に接続されているため、 Module:から直接取得できます。

ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();

3.2. ModuleDescriptorの作成

ModuleDescriptor.Builderクラスを使用するか、モジュール宣言のバイナリ形式 module-info.class を読み取ることにより、モジュール記述子を作成することもできます。

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

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. 取得が必要

モジュール記述子を使用すると、モジュールの依存関係を表すRequiredsのセットを取得できます。

これは、 require()メソッドを使用して可能です。

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(javaSqlRequiresNames, hasItems("java.base", "java.xml", "java.logging"));

javabaseを除くすべてのモジュールには、依存関係としてjavabaseモジュールがあります。

ただし、モジュールが自動モジュールの場合、 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, hasItem("java.nio.file.spi.FileSystemProvider"));
assertThat(javaSqlProvides, empty());

3.6. エクスポートの取得

exports()メソッドを使用して、モジュールがパッケージをエクスポートするかどうか、特に次のいずれかを確認できます。

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

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

assertThat(javaSqlExportsSource, hasItems("java.sql", "javax.sql"));

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

3.7. 使用の取得

used()メソッドを使用すると、モジュールのサービス依存関係のセットを取得できます。

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

assertThat(javaSqlUses, hasItem("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. 結論

この記事では、 java.lang.Module APIの使用方法について説明し、モジュールの情報を取得する方法、ModuleDescriptorを使用してモジュールとその操作方法。

いつものように、この記事のすべてのコード例は、GitHubにあります。