Modularität in Java 9

Die Hauptinnovation in Java 9 war die Einführung der Modularität. Es wurde viel über diese Funktion gesprochen, der Veröffentlichungstermin wurde mehrmals verschoben, um alles richtig zu beenden. In diesem Beitrag werden wir darüber sprechen, was den Mechanismus von Modulen gibt und was nützliches Java 9 im Allgemeinen bringt. Grundlage für die Stelle war der Bericht meines Kollegen Sergei Malkevich .




Um die Module in dieser Java-Version zu implementieren, wurde ein ganzes Projekt zugewiesen - Project Jigsaw - das mehrere JEP und JSR enthält.



Für Fans der offiziellen Dokumentation können Sie hier mehr über jeden JEP lesen .

Mehr zum Projekt Puzzle


Das Projekt Jigsaw, das Modularität implementiert, wurde bereits 2005 entwickelt: Zuerst wurde JSR 277 veröffentlicht, und bereits 2008 begannen die direkten Arbeiten an dem Projekt. Die Veröffentlichung erfolgte erst 2017. Das heißt, um die Module in Java zu verschrauben, dauerte es fast 10 Jahre. Dies unterstreicht in der Tat den vollen Umfang der Arbeit und die Änderungen, die während der Implementierung der Modularität vorgenommen wurden.

Welche Ziele setzen sich die Entwickler:

  • Erleichterung der Entwicklung großer Anwendungen und Bibliotheken;
  • Verbesserung der Sicherheit von Java SE im Allgemeinen und von JDK im Besonderen;
  • die Anwendungsleistung steigern;
  • Schaffung der Möglichkeit, die Größe der JRE für die Ausführung auf kleinen Geräten zu verringern, um nicht zu viel Speicher zu verbrauchen;
  • JAR HELL (dazu später mehr).

Was nützliches Java 9 gebracht hat


Vor Version 9 waren JDK und JRE monolithisch. Ihre Größe wuchs mit jeder Veröffentlichung. Java 8 belegte bereits Hunderte von Megabyte, und all dies mussten die Entwickler jedes Mal „mit sich führen“, um Java-Anwendungen ausführen zu können. Allein rt.jar wiegt etwa 60 Mb. Nun, hier fügen wir auch einen langsamen Start und einen hohen Speicherverbrauch hinzu. Java 9 kam zur Rettung.

In JDK 9Die Modultrennung wurde eingeführt, nämlich das JDK wurde in 73 Module unterteilt. Und mit jeder neuen Version wächst die Anzahl dieser Module. In Version 11 liegt diese Zahl nahe bei 100. Diese Trennung ermöglichte es Entwicklern, das JLINK-Dienstprogramm zu erstellen. Mit JLINK können Sie benutzerdefinierte JRE-Sets erstellen, die nur die „erforderlichen“ Module enthalten, die Ihre Anwendung wirklich benötigt. Somit können eine einfache Anwendung und einige benutzerdefinierte JRE mit einem minimalen (oder kleinen) Satz von Modulen schließlich in 20 MB passen, was eine gute Nachricht ist.

Die Liste der Module finden Sie hier .

Mit dem Aufkommen von Java 9 hat sich die JDK-Struktur geändert: Jetzt ist sie identisch mit der JRE-Struktur. Wenn das JDK früher den JRE-Ordner enthielt, in dem sich wieder bin befindet und Dateien dupliziert werden, sieht jetzt alles so aus:



Module


Tatsächlich. Was ist ein Modul? Ein Modul ist eine neue Ebene der Paket- und Ressourcenaggregation (ursprünglich „Eine eindeutig benannte, wiederverwendbare Gruppe verwandter Pakete sowie Ressourcen und ein Moduldeskriptor“ ).

Module werden in JAR-Dateien mit Paketen und
Modulbeschreibung module-info.java geliefert . Die Datei module-info.java enthält eine Beschreibung des Moduls:
Name, Abhängigkeiten, exportierte Pakete, verbrauchte und bereitgestellte Dienste, Berechtigungen für den Reflection-Zugriff.

Beispiele für Moduldeskriptorbeschreibungen:

module java.sql {
    requires transitive java.logging;
    requires transitive java.transaction.xa;
    requires transitive java.xml;

    exports java.sql;
    exports javax.sql;

    uses java.sql.Driver;
}

module jdk.javadoc {
   requires java.xml;
   
   requires transitive java.compiler;
   requires transitive jdk.compiler;
   
   exports jdk.javadoc.doclet;
   
   provides java.util.spi.ToolProvider with
       jdk.javadoc.internal.tool.JavadocToolProvider;
   
   provides javax.tools.DocumentationTool with
       jdk.javadoc.internal.api.JavadocTool;
   
   provides javax.tools.Tool with
      jdk.javadoc.internal.api.JavadocTool;   
}

Nach dem Schlüsselwortmodul haben wir den Namen des Pakets jdk.javadoc , der von einem anderen Paket java.xml und transitiv von anderen Paketen abhängt.

Schauen wir uns die einzelnen Schlüsselwörter genauer an:

  • erfordert gibt die Module an, von denen das aktuelle Modul abhängt;

  • erfordert transitiv - eine transitive Abhängigkeit - bedeutet Folgendes: Wenn das Modul m1 transitiv vom Modul m2 abhängig ist und wir ein drittes Modul mX haben , das von m1 abhängt, hat das Modul mX auch Zugriff auf m2 ;

  • erfordert statisch ermöglicht es Ihnen, Abhängigkeiten zur Kompilierungszeit anzugeben;

  • exports , ( “”);

  • exports...to… : export com.my.package.name to com.specific.package; - () () ;

  • uses , :

    uses java.sql.Driver;

    , ;

  • provides , :

    provides javax.tools.Tool with
        jdk.javadoc.internal.api.JavadocTool;

    javax.tools.Tool, with — .

Ein wenig über Services.

Nehmen wir an, wir haben mehrere Module verbunden, die einen abstrakten Service implementieren - MyService . Beim Erstellen der Anwendung haben wir die Möglichkeit zu entscheiden, welche Service-Implementierung verwendet werden soll, indem wir die erforderlichen Service-Implementierungsmodule auf --module-path ziehen :

Iterable<MyService> services = 
        ServiceLoader.load(MyService.class);

Daher enthält der zurückgegebene Iterator eine Liste der Implementierungen der MyService-Schnittstelle. Tatsächlich enthält es alle Implementierungen, die in den Modulen auf --module-path enthalten sind .

Warum wurden grundsätzlich Dienstleistungen eingeführt? Sie werden benötigt, um zu zeigen, wie unser Code verwendet wird. Das heißt, es gibt eine semantische Rolle. Bei Modularität geht es auch um Kapselung und Sicherheit, da wir die Implementierung privat machen und die Möglichkeit eines nicht autorisierten Zugriffs durch Reflexion ausschließen können.

Eine der Optionen für die Verwendung von Diensten ist eine relativ einfache Implementierung von Plugins. Wir können die Plugin-Schnittstelle für unsere Anwendung implementieren und Module verbinden, um mit ihnen zu arbeiten.

Kehren wir zur Syntax zur Beschreibung der Module zurück:

Bis 9ki hatten wir durch Nachdenken Zugang zu fast allem und konnten tun, was wir wollen und mit dem, was wir wollen. Und die 9. Version ermöglicht es Ihnen, wie bereits erwähnt, sich vor „illegalem“ Zugriff auf Reflexionen zu schützen.

Wir können das Modul für den Reflexionszugriff vollständig öffnen, indem wir es für offen erklären :

open module my.module {
}

Oder wir können beliebige Pakete für den Reflection-Zugriff angeben, indem wir " opens" deklarieren :

module my.module {
    opens com.my.coolpackage;
}

Hier ist es auch möglich, öffnet com.my.coolpackage zu ... , wodurch Reflexions Zugang zum com.my.coolpackage Paket aus dem Paket , dass wir nach der anzeigt an .

Modultypen


Project Jigsaw klassifiziert Module wie folgt:

  • System Modules — Java SE JDK . , java --list-modules.

  • Application Modules — , , ( ), .

  • Automatic Modules — , Java JAR-. , , - . JAR- --module-path Java , JAR-.

  • Unnamed Module — , JAR-, --class-path. Java .

Class-path vs module-path


Mit dem Aufkommen der Module erschien ein neues Konzept - der Modulpfad . Im Wesentlichen ist dies der gleiche Klassenpfad , jedoch für Module.

Der Start einer modularen Anwendung erfolgt wie folgt:



Im normalen Startmodus geben wir die Optionen und den vollständigen Pfad zur Hauptklasse an. Wenn wir mit Modulen arbeiten möchten, geben wir auch Optionen und den Parameter -m oder -module an , der nur angibt, dass wir die Module ausführen werden. Das heißt, wir übersetzen unsere Anwendung automatisch in den modularen Modus. Als nächstes geben wir den Namen des Moduls und den Pfad zur Hauptklasse vom Modul an.

Wenn wir uns im normalen Modus befinden, sind wir es gewohnt, mit den Optionen -cp und --class-path zu arbeitenIm modularen Modus schreiben wir einen neuen Parameter -p und --module-path vor , der die Pfade zu den in der Anwendung verwendeten Modulen angibt.

Oft stoße ich auf die Tatsache, dass Entwickler nicht auf Version 9+ wechseln, weil sie glauben, dass sie mit Modulen arbeiten müssen. Tatsächlich können wir unsere Anwendungen im alten Modus ausführen, ohne einen Parameter zu schreiben oder Module zu verwenden, sondern nur andere neue Chips.

Jar Hölle


Ich möchte auch diagonal auf das Jar Hell-Problem eingehen.



Was ist Jar Hell auf den Punkt gebracht? Zum Beispiel haben wir eine Art unserer Anwendung, die von der X- Bibliothek und der Y-Bibliothek abhängt . Gleichzeitig hängen beide Bibliotheken von der Z-Bibliothek ab , jedoch von unterschiedlichen Versionen: X hängt von Version 1 ab , Y von Version 2 . Nun, wenn Version 2 abwärtskompatibel mit Version 1 ist, dann kein Problem. Wenn nicht, ist es offensichtlich, dass es zu einem Versionskonflikt kommt, dh, dieselbe Bibliothek kann nicht von demselben Klassenladeprogramm in den Speicher geladen werden.

Wie kommst du aus dieser Situation heraus? Es gibt Standardmethoden, die Entwickler seit dem ersten Java verwenden, z. B. ausschließen , jemand verwendet Plugins für Maven, die die Namen der Stammpakete der Bibliothek umbenennen. Oder Entwickler suchen nach verschiedenen Versionen der X-Bibliothek , um eine kompatible Option zu finden.

Warum bin ich: Die ersten Jigsaw-Prototypen implizierten, dass das Modul eine Version hatte und das Laden mehrerer Versionen über verschiedene ClassLoader ermöglichte, aber später wurde es aufgegeben. Infolgedessen funktionierte die „Silberkugel“, auf die viele warteten, nicht.

Aber sofort waren wir vor solchen Problemen ein wenig sicher. Java 9 Deaktiviert geteilte Pakete- Pakete, die in mehrere Module unterteilt sind. Das heißt, wenn wir das Paket com.my.coolpackage in einem Modul haben, können wir es nicht in einem anderen Modul innerhalb derselben Anwendung verwenden. Wenn Sie die Anwendung mit Modulen starten, die dieselben Pakete enthalten, stürzen wir einfach ab. Diese kleine Verbesserung eliminiert die Möglichkeit eines unvorhersehbaren Verhaltens beim Herunterladen von Split-Paketen.

Zusätzlich zu den Modulen selbst gibt es auch einen Ebenenmechanismus oder Puzzle-Ebenen , der auch zur Bewältigung des Jar Hell-Problems beiträgt.

Eine Puzzle-Schicht kann als ein lokales modulares System definiert werden. Und hier ist anzumerken, dass die oben genannten Split-Pakete nur im Rahmen einer Jigsaw-Schicht verboten sind. Module mit denselben Paketen haben einen Platz, müssen aber zu verschiedenen Ebenen gehören.

Es sieht folgendermaßen aus:



Wenn die Anwendung gestartet wird, wird die Boot- Schicht erstellt , die die von Bootstrap geladenen Plattformmodule, zusätzliche vom Plattformlader geladene Plattformmodule und unsere vom Anwendungslader geladenen Anwendungsmodule enthält.

Wir können jederzeit unsere eigenen Ebenen erstellen und dort Module verschiedener Versionen „platzieren“ und nicht fallen.

Es gibt einen großartigen, detaillierten YouTube-Vortrag zum Thema: Jar Hell mit Jigsaw Layers entkommen

Fazit


Die Modul-Engine aus Java 9 eröffnet uns neue Möglichkeiten, während die Unterstützung für Bibliotheken heute recht gering ist. Ja, die Leute laufen Spring, Spring Boot und so weiter. Die meisten Bibliotheken haben jedoch nicht auf die volle Nutzung von Modulen umgestellt. Offensichtlich wurden all diese Änderungen von der technischen Gemeinschaft eher skeptisch wahrgenommen. Module bieten uns neue Möglichkeiten, aber die Frage der Nachfrage bleibt offen.

Und schließlich biete ich eine Auswahl von Materialien zu diesem Thema an: Zusammenfassung der JDK-Module von

Project Jigsaw Paul Deitel - Grundlegendes zu Java 9-Modulen baeldung.com - Einführung in Project Jigsaw Alex Buckley - Modulare Entwicklung mit JDK 9 Evgeny Kozlov - Module in Java










All Articles