Keine Ergebnisse gefunden

Ihre Suche ergab keine Treffer.

Beachten Sie die folgenden Tipps, um das Gesuchte zu finden:

  • Prüfen Sie die Schreibweise des Suchbegriffs.
  • Verwenden Sie Synonyme für das eingegebene Stichwort, z. B. “Anwendung” statt “Software.”
  • Testen Sie eine der unten gezeigten beliebten Suchen.
  • Beginnen Sie eine neue Suche.
Aktuelle Fragen

 

Java 9 | Auszug

Java 9 Modules verstehen

Was sie sind und wie sie verwendet werden

Von Paul Deitel


Paul Deitel

Paul Deitel

In diesem Artikel stelle ich das Java 9 Platform Module System (JPMS) vor, die wichtigste neue Software-Engineering-Technologie in Java seit ihrer Einführung. Modularität – das Ergebnis von Project Jigsaw – hilft Entwicklern auf allen Ebenen, bei der Erstellung, Wartung und Entwicklung von Softwaresystemen, insbesondere großen Systemen, produktiver zu sein.

Was ist ein Modul?

Durch die Modularität wird eine höhere Aggregationsebene als bei Paketen erreicht. Das wichtigste neue Sprachelement ist das Modul – eine eindeutig benannte, wiederverwendbare Gruppe von zugehörigen Paketen sowie Ressourcen (wie Bilder und XML-Dateien) und ein Moduldeskriptor, der folgende Angaben enthält:

  • Name des Moduls
  • Abhängigkeiten des Moduls (andere Module, von denen dieses Modul abhängig ist)
  • Pakete, die es explizit für andere Module verfügbar macht (alle anderen Packages im Modul sind für andere Module implizit nicht verfügbar).
  • Services, die es anbietet
  • Services, die es nutzt
  • andere Module, zu denen es Reflexion ermöglicht

Geschichte

Die Java SE-Plattform existiert bereits seit 1995. Mittlerweile verwenden etwa 10 Millionen Entwickler alles, von kleinen Apps für ressourcenbeschränkte Geräte – wie die im Internet of Things (IoT) und anderen eingebetteten Geräten – bis hin zu großen geschäftskritischen und unternehmenskritischen Systemen. Es gibt riesige Mengen an Legacy-Code, aber bisher war die Java-Plattform in erster Linie eine monolithische One-Size-Fits-All-Lösung. Im Laufe der Jahre gab es verschiedene Bemühungen zur Modularisierung von Java, aber keine ist weit verbreitet – und keine konnte zur Modularisierung der Java-Plattform verwendet werden.

Die Modularisierung der Java SE-Plattform war eine anspruchsvolle Herausforderung und hat viele Jahre in Anspruch genommen. JSR 277: Java Module System wurde ursprünglich 2005 für Java 7 vorgeschlagen. Diese JSR wurde später durch JSR 376: Java Platform Module System ersetzt und war für Java 8 vorgesehen. Die Java SE-Plattform ist jetzt in Java 9 modularisiert, aber erst nachdem Java 9 bis September 2017 verschoben wurde.

Ziele

Jedes Modul muss seine Abhängigkeiten explizit angeben.

Laut JSR 376 sind die Hauptziele der Modularisierung der Java SE-Plattform folgende:

  • Zuverlässige Konfiguration – Modularität bietet Mechanismen zur expliziten Deklaration von Abhängigkeiten zwischen Modulen in einer Weise, die sowohl zur Kompilier- als auch zur Ausführungszeit erkannt wird. Das System kann diese Abhängigkeiten überprüfen, um den Teil aller Module zu bestimmen, die zur Unterstützung Ihrer Anwendung erforderlich sind.
  • Starke Kapselung – Die Pakete in einem Modul sind nur dann für andere Module zugänglich, wenn das Modul sie ausdrücklich exportiert. Selbst dann kann ein anderes Modul diese Pakete nicht verwenden, es sei denn, es gibt ausdrücklich an, dass es die Fähigkeiten des anderen Moduls benötigt. Dies verbessert die Plattformsicherheit, da potenziellen Angreifern weniger Klassen zugänglich sind. Sie werden feststellen, dass die Berücksichtigung der Modularität Ihnen hilft, sauberere und logischere Entwürfe zu erstellen.
  • Skalierbare Java-Plattform – Früher war die Java-Plattform ein Monolith, der aus einer riesigen Anzahl von Paketen bestand, was die Entwicklung, Wartung und Weiterentwicklung erschwerte. Sie kann nicht einfach unterteilt werden. Die Plattform ist jetzt in 95 Module unterteilt (diese Zahl kann sich mit der weiteren Entwicklung von Java ändern). Sie können benutzerdefinierte Laufzeiten erstellen, die nur aus Modulen bestehen, die Sie für Ihre Anwendungen oder die Geräte, auf die Sie abzielen, benötigen. Wenn ein Gerät beispielsweise keine grafischen Benutzeroberflächen unterstützt, können Sie eine Laufzeitumgebung erstellen, die keine grafischen Benutzeroberflächenmodule enthält, wodurch sich die Größe der Laufzeitumgebung erheblich verringert.
  • Mehr Plattformintegrität – Vor Java 9 war es möglich, viele Klassen in der Plattform zu verwenden, die nicht für die Verwendung durch die Klassen einer Anwendung bestimmt waren. Mit einer starken Kapselung sind diese internen APIs nun wirklich gekapselt und für Anwendungen, die die Plattform nutzen, verborgen. Das kann die Migration von Legacy-Code auf das modularisierte Java 9 erschweren, wenn Ihr Code von internen APIs abhängig ist.
  • Verbesserte Performance – Die JVM verwendet verschiedene Optimierungstechniken, um die Anwendungsperformance zu verbessern. JSR 376 gibt an, dass diese Techniken effektiver sind, wenn im Voraus bekannt ist, dass sich erforderliche Typen nur in bestimmten Modulen befinden.

Auflistung der JDK-Module

JEP 200: DAS MODULARE JDK

JEP 201: MODULARER QUELLCODE

JEP 220: MODULARE LAUFZEIT-IMAGES

JEP 260: KAPSELUNG DER MEISTEN INTERNEN APIS

JEP 261: MODULSYSTEM

JEP 275: MODULARES PACKAGING VON JAVA-ANWENDUNGEN

JEP 282: JLINK: DER JAVA-LINKER

JSR 376: JAVA PLATFORM-MODULSYSTEM

JSR 379: JAVA SE 9

Tabelle 1. JEPs und JSRs für Java Modularity

Ein wichtiger Aspekt von Java 9 ist die Aufteilung des JDK in Module zur Unterstützung verschiedener Konfigurationen. (siehe „JEP 200: Das modulare JDK“. Alle JEPs und JSRs der Java-Modularität werden in Tabelle 1 dargestellt.) Verwenden Sie den java-Befehl aus dem bin-Ordner des JDK mit der Option --list-modules wie folgt: 

java --list-modules

listet den Modulsatz des JDK auf, der die Standardmodule enthält, die die Java Language SE-Spezifikation implementieren (Namen beginnen mit java), JavaFX-Module (Namen beginnen mit javafx), JDK -spezifische Module (Namen beginnen mit jdk) und Oracle-spezifische Module (Namen beginnen mit oracle). Auf jeden Modulnamen folgt eine Versionszeichenfolge – @9 gibt an, dass das Modul zu Java 9 gehört.

Moduldeklarationen

Wie bereits erwähnt, muss ein Modul einen Moduldeskriptor bereitstellen – Metadaten, die die Abhängigkeiten des Moduls, die Pakete, die das Modul anderen Modulen zur Verfügung stellt, und mehr angeben. Ein Moduldeskriptor ist die kompilierte Version einer Moduldeklaration, die in einer Datei mit dem Namen module-info.java definiert ist. Jede Modulanmeldung beginnt mit dem Schlüsselwort module, gefolgt von einem eindeutigen Modulnamen und einem in Klammern eingeschlossenen Modulhauptteil, wie folgt:

Eine wesentliche Motivation des Modulsystems ist eine starke Kapselung.

module modulename
}

Der Hauptteil der Moduldeklaration kann leer sein oder verschiedene Moduldirektiven enthalten, darunter requires, exports, provides…with, uses und opens (die wir jeweils erläutern). Wie Sie später sehen werden, wird beim Kompilieren der Moduldeklaration der Moduldeskriptor erstellt, der in einer Datei namens module-info.class im Stammverzeichnis des Moduls gespeichert wird. Hier wird jede Moduldirektive kurz vorgestellt. Danach folgen die eigentlichen Moduldeklarationen.

Die Schlüsselwörter exports, module, open, opens, provides, requires, uses, with sowie to und transitive, die wir später vorstellen werden, sind eingeschränkte Schlüsselwörter. Sie gelten nur in Moduldeklarationen als Schlüsselwörter und können überall sonst in Ihrem Code als Bezeichner verwendet werden.

requires. Ein requires-Moduldirektiv gibt an, dass dieses Modul von einem anderen Modul abhängig ist – diese Beziehung wird als Modulabhängigkeit bezeichnet. Jedes Modul muss seine Abhängigkeiten explizit angeben. Wenn Modul A von Modul B abhängig ist (Modul A requires Modul B), liest Modul A Modul B (Modul A read Modul B) und Modul B wird von Modul A gelesen (Modul B read by Modul A). Um eine Abhängigkeit von einem anderen Modul anzugeben, verwenden Sie requires wie folgt:

requires modulename;

Außerdem gibt die Direktive requires static an, dass ein Modul zur Kompilierungszeit erforderlich ist, jedoch zur Laufzeit optional ist. Das ist eine so genannte optionale Abhängigkeit und wird in dieser Einführung nicht behandelt.

requires transitive – implizite Lesbarkeit. Um eine Abhängigkeit von einem anderen Modul anzugeben und sicherzustellen, dass andere Module, die Ihr Modul lesen, auch diese Abhängigkeit (auch als implied readability bezeichnet) lesen, verwenden Sie requires transitive, wie folgt:

requires transitive modulename;

Betrachten Sie die folgende Direktive aus der Moduldeklaration java.desktop:

requires transitive java.xml

In diesem Fall liest jedes Modul, das java.desktop liest, auch implizit java.xml. Wenn eine Methode beispielsweise aus dem Modul java.desktop einen Typ aus dem Modul java.xml zurückgibt, wird Code in Modulen, die java.desktop lesen, von java.xml abhängig. Ohne die Direktive requires transitive in der Moduldeklaration von java.desktop werden solche abhängigen Module nicht kompiliert, es sei denn, sie lesen java.xml explizit.

Gemäß JSR 379 müssen die Standardmodule von Java SE in allen Fällen wie dem hier beschriebenen implizite Lesbarkeit gewährleisten. Auch wenn ein Java SE-Standardmodul von Nicht-Standardmodulen abhängen kann, darf es ihnen keine implizite Lesbarkeit gewähren. Dadurch wird sichergestellt, dass Code, der nur von Java SE-Standardmodulen abhängt, über Java SE-Implementierungen hinweg portierbar ist.

exports und exports…to. Eine exports-Moduldirektive gibt eines der Module des Moduls an, dessen public-Typen (und ihre verschachtelten public- und protected-Typen) für den Code in allen anderen Modulen zugänglich sein sollten. Mit einer exports…to-Direktive können Sie in einer durch Kommas getrennten Liste genau angeben, welches Modul oder welcher Modulcode auf das exportierte Paket zugreifen kann – dies wird als qualifizierter Export bezeichnet.

uses. Eine uses-Moduldirektive gibt einen Service an, der von diesem Modul verwendet wird. Dadurch wird das Modul zu einem Service Consumer. Ein Service ist ein Objekt einer Klasse, das die Schnittstelle implementiert oder die in der Direktive uses angegebene Klasse abstract erweitert.

provides…with. Eine provides…with-Moduldirektive gibt an, dass ein Modul eine Serviceimplementierung bereitstellt – wodurch das Modul zu einem Serviceprovider wird. Der provides-Teil der Direktive gibt eine Schnittstelle oder abstract-Klasse an, die in der uses-Direktive eines Moduls aufgeführt ist, und der with-Teil der Direktive gibt den Namen der Serviceproviderklasse an, die die Schnittstelle implementiert (implements) oder die abstract-Klasse erweitert (extends).

open, opens und opens…to. Vor Java 9 konnte Reflexion verwendet werden, um mehr über alle Typen in einem Paket und alle Mitglieder eines Typs zu erfahren – sogar über seine private (privaten) Mitglieder – unabhängig davon, ob Sie diese Fähigkeit zulassen wollten oder nicht. Es wurde also nichts wirklich gekapselt.

Eine wesentliche Motivation des Modulsystems ist eine starke Kapselung. Standardmäßig ist ein Typ in einem Modul für andere Module nicht zugänglich, es sei denn, er ist ein öffentlicher Typ und Sie exportieren sein Paket. Sie legen nur die Pakete offen, die Sie offenlegen wollen. In Java 9 gilt das auch für die Reflexion.

Nur Laufzeitzugriff auf ein Paket zulassen. Eine opens-Modulrichtlinie der Maske

opens package

gibt an, dass die public-Typen eines bestimmten Pakets (und ihre verschachtelten public- und protected-Typen) nur zur Laufzeit für Code in anderen Modulen zugänglich sind. Außerdem sind alle Typen im angegebenen Paket (und alle Mitglieder der Typen) über Reflexion zugänglich.

Nur Laufzeitzugriff auf ein Paket durch bestimmte Module zulassen. Eine Moduldirektive opens…to der Form

opens package to comma-separated-list-of-modules

gibt an, dass die public-Typen eines bestimmten Pakets (und ihre verschachtelten public- und protected-Typen) nur zur Laufzeit für Code in den aufgelisteten Modulen zugänglich sind. Alle Typen im angegebenen Paket (und alle Mitglieder der Typen) sind über Reflexion für den Code in den angegebenen Modulen zugänglich.

Nur Laufzeitzugriff auf alle Pakete in einem Modul zulassen. Wenn alle Pakete eines Moduls zur Laufzeit und über Reflexion für alle anderen Module zugänglich sein sollen, können Sie das gesamte Modul öffnen (open), z. B. wie folgt:

open module modulename {
   // module directives

Reflexionsstandardwerte

Standardmäßig kann ein Modul mit reflexivem Laufzeitzugriff auf ein Package die public-Typen des Packages (und ihre verschachtelten public- und protected-Typen) sehen. Der Code in anderen Modulen kann jedoch auf alle Typen im angegebenen Paket und alle Mitglieder innerhalb dieser Typen zugreifen, einschließlich privater (private) Mitglieder über setAccessible, wie in früheren Java-Versionen.

Weitere Informationen zu setAccessible und zur Reflexion finden Sie in der Dokumentation von Oracle.


Paul Deitel, CEO und Chief Technical Officer von Deitel & Associates, ist Absolvent des MIT mit 35 Jahren Erfahrung in der Informatik. Er ist ein Java Champion und programmiert seit mehr als 22 Jahren in Java. Er und sein Mitautor Dr. Harvey M. Deitel, sind die weltweit bekannte Bestseller-Autoren für Programmiersprachen. Paul hat weltweit Java-, Android-, iOS-, C#-, C++-, C- und Internet-Programmierkurse für Industrie-, Regierungs- und akademische Kunden durchgeführt.


HINWEIS: Dieser Artikel ist ein Auszug aus dem Java Magazine, Ausgabe September/Oktober 2017.

Mehr erfahren