Java 9 java.lang.Module API

Java 9 java.lang.Module API

1. Einführung

NachA Guide to Java 9 Modularity werden wir in diesem Artikel diejava.lang.Module-API untersuchen, die zusammen mit dem Java Platform Module System eingeführt wurde.

Diese API bietet eine Möglichkeit, programmgesteuert auf ein Modul zuzugreifen, bestimmte Informationen von einem Modul abzurufen und im Allgemeinen mit ihm und seinenModuleDescriptor zu arbeiten.

2. Modulinformationen lesen

Die KlasseModulerepräsentiert sowohl benannte als auch unbenannte Module. Named modules have a name and are constructed by the Java Virtual Machine when it creates a module layer, verwendet ein Diagramm von Modulen als Definition.

Ein unbenanntes Modul hat keinen Namen und es gibt einen für jedesClassLoader.All types that aren’t in a named module are members of the unnamed module related to their class loader.

Der interessante Teil der KlasseModulebesteht darin, dass Methoden verfügbar gemacht werden, mit denen wir Informationen aus dem Modul abrufen können, z. B. den Modulnamen, den Modulklassenlader und die Pakete innerhalb des Moduls.

Mal sehen, wie man herausfinden kann, ob ein Modul benannt oder nicht benannt ist.

2.1. Benannt oder unbenannt

Mit der MethodeisNamed()können wir feststellen, ob ein Modul benannt ist oder nicht.

Mal sehen, wie wir sehen können, ob eine bestimmte Klasse wieHashMap Teil eines benannten Moduls ist und wie wir ihren Namen abrufen können:

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

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

Definieren wir nun einePerson-Klasse:

public class Person {
    private String name;

    // constructor, getters and setters
}

Auf die gleiche Weise wie bei der KlasseHashMapkönnen wir überprüfen, ob die KlassePersonTeil eines benannten Moduls ist:

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

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

2.2. Pakete

Bei der Arbeit mit einem Modul ist es möglicherweise wichtig zu wissen, welche Pakete im Modul verfügbar sind.

Lassen Sie uns sehen, wie wir überprüfen können, ob ein bestimmtes Paket, z. B.java.lang.annotation, in einem bestimmten Modul enthalten ist:

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

2.3. Anmerkungen

Auf die gleiche Weise wie bei den Paketenit’s possible to retrieve the annotations that are present in the module using the getAnnotations() method.

Wenn in einem benannten Modul keine Anmerkungen vorhanden sind, gibt die Methode ein leeres Array zurück.

Mal sehen, wie viele Anmerkungen im Moduljava.base vorhanden sind:

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

Beim Aufruf eines unbenannten Moduls gibt die MethodegetAnnotations()ein leeres Array zurück.

2.4. ClassLoader

Dank dergetClassLoader()-Methode, die in derModule-Klasse verfügbar ist, können wir dieClassLoader für ein bestimmtes Modul abrufen:

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

2.5. Schicht

Eine weitere wertvolle Information, die aus einem Modul extrahiert werden könnte, istModuleLayer, das eine Schicht von Modulen in der virtuellen Java-Maschine darstellt.

Eine Modulschicht informiert die JVM über die Klassen, die aus den Modulen geladen werden können. Auf diese Weise weiß die JVM genau, zu welchem ​​Modul jede Klasse gehört.

EinModuleLayer enthält Informationen zu seiner Konfiguration, der übergeordneten Ebene und der Menge der in der Ebene verfügbaren Module.

Mal sehen, wie man dieModuleLayer eines bestimmten Moduls abruft:

ModuleLayer javaBaseModuleLayer = javaBaseModule.getLayer();

Sobald wir dieModuleLayer abgerufen haben, können wir auf ihre Informationen zugreifen:

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

Ein Sonderfall ist die Startschicht, die beim Starten der Java Virtual Machine erstellt wird. Die Boot-Schicht ist die einzige Schicht, die das Moduljava.baseenthält.

3. Umgang mitModuleDescriptor

AModuleDescriptor beschreibt ein benanntes Modul und definiert Methoden, um jede seiner Komponenten zu erhalten.

ModuleDescriptor Objekte sind unveränderlich und für die Verwendung durch mehrere gleichzeitige Threads sicher.

Schauen wir uns zunächst an, wie wirModuleDescriptor. abrufen können

3.1. Abrufen vonModuleDescriptor

DaModuleDescriptor eng mitModule verbunden ist, kann es direkt vonModule: abgerufen werden

ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();

3.2. ModuleDescriptor erstellen

It’s also possible to create a module descriptor using the ModuleDescriptor.Builder class oder durch Lesen der Binärform einer Moduldeklaration diemodule-info.class.

Lassen Sie uns sehen, wie wir mithilfe derModuleDescriptor.Builder-API einen Moduldeskriptor erstellen:

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

ModuleDescriptor moduleDescriptor = moduleBuilder.build();

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

Damit haben wir ein normales Modul erstellt, aber falls wir ein offenes oder ein automatisches Modul erstellen möchten, können wir jeweils die MethodenewOpenModule() odernewAutomaticModule() verwenden.

3.3. Ein Modul klassifizieren

Ein Moduldeskriptor beschreibt ein normales, offenes oder automatisches Modul.

Dank der inModuleDescriptor verfügbaren Methode ist es möglich, den Typ des Moduls zu identifizieren:

ModuleDescriptor moduleDescriptor = javaBaseModule.getDescriptor();

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

3.4. Abrufen erforderlich

Mit einem Moduldeskriptor ist es möglich, die Menge vonRequires abzurufen, die die Modulabhängigkeiten darstellt.

Dies ist mit der Methoderequires()möglich:

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

Set 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")
);

All modules, except java.base, have the java.base module as a dependency.

Wenn das Modul jedoch ein automatisches Modul ist, ist der Satz von Abhängigkeiten mit Ausnahme desjava.base-Moduls leer.

3.5. Abrufen von Angeboten

Mit derprovides()-Methode können Sie die Liste der vom Modul bereitgestellten Dienste abrufen:

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

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

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

3.6. Exporte abrufen

Mit der Methodeexports()können wir herausfinden, ob die Module Pakete exportieren und welche insbesondere:

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

Set 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")
);

In einem speziellen Fall, wenn es sich um ein automatisches Modul handelt, ist der Satz der exportierten Pakete leer.

3.7. Verwendungen abrufen

Mit der Methodeuses() ist es möglich, die Dienstabhängigkeiten des Moduls abzurufen:

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

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

Falls es sich um ein automatisches Modul handelt, sind die Abhängigkeiten leer.

3.8. Abrufen Öffnet

Wann immer wir die Liste der offenen Pakete eines Moduls abrufen möchten, können wir die Methodeopens()verwenden:

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

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

Das Set ist leer, wenn das Modul offen oder automatisch ist.

4. Umgang mit Modulen

Wenn wir mit derModule-API arbeiten und keine Informationen aus dem Modul lesen, können wir eine Moduldefinition aktualisieren.

4.1. Exporte hinzufügen

Mal sehen, wie wir ein Modul aktualisieren und das angegebene Paket aus einem bestimmten Modul exportieren können:

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

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

Dies ist nur möglich, wenn das Modul des Anrufers das Modul ist, zu dem der Code gehört.

Nebenbei bemerkt, es gibt keine Auswirkungen, wenn das Paket bereits vom Modul exportiert wurde oder wenn das Modul offen ist.

4.2. Lesevorgänge hinzufügen

Wenn wir ein Modul aktualisieren möchten, um ein bestimmtes Modul zu lesen, können wir die MethodeaddReads()verwenden:

Module updatedModule = module.addReads(javaSqlModule);

assertTrue(updatedModule.canRead(javaSqlModule));

Diese Methode macht nichts, wenn wir das Modul selbst hinzufügen, da alle Module sich selbst lesen.

Auf die gleiche Weise führt diese Methode nichts aus, wenn das Modul ein unbenanntes Modul ist oder dieses Modul das andere Modul bereits liest.

4.3. Hinzufügen Öffnet

Wenn wir ein Modul aktualisieren möchten, das ein Paket für mindestens das Aufrufermodul geöffnet hat, können wiraddOpens() verwenden, um das Paket für ein anderes Modul zu öffnen:

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

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

Diese Methode hat keine Auswirkung, wenn das Paket für das angegebene Modul bereits geöffnet ist.

4.4. Verwendungen hinzufügen

Wann immer wir ein Modul aktualisieren möchten, das eine Dienstabhängigkeit hinzufügt, ist die MethodeaddUses() unsere Wahl:

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

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

Diese Methode führt nichts aus, wenn sie für ein unbenanntes Modul oder ein automatisches Modul aufgerufen wird.

5. Fazit

In diesem Artikel haben wir die Verwendung derjava.lang.Module-API untersucht, in der wir gelernt haben, wie Informationen eines Moduls abgerufen werden, wie mitModuleDescriptor auf zusätzliche Informationen zu einem Modul zugegriffen wird und wie diese bearbeitet werden.

Wie immer finden Sie alle Codebeispiele in diesem Artikel inover on GitHub.