Modularität in Java 9: ​​Stapeln mit Project Jigsaw, Penrose und OSGi

Dieser Artikel bietet einen Überblick über Vorschläge, Spezifikationen und Plattformen, die darauf abzielen, die Java-Technologie in Java 9 modularer zu gestalten. Ich werde Faktoren erörtern, die zur Notwendigkeit einer modulareren Java-Architektur beitragen, die vorgeschlagenen Lösungen kurz beschreiben und vergleichen. und stellen Sie die drei für Java 9 geplanten Modularitätsaktualisierungen vor, einschließlich ihrer möglichen Auswirkungen auf die Java-Entwicklung.

Warum brauchen wir Java-Modularität?

Modularität ist ein allgemeines Konzept. In der Software gilt dies für das Schreiben und Implementieren eines Programms oder Computersystems als eine Reihe eindeutiger Module und nicht als einzelnes monolithisches Design. Eine standardisierte Schnittstelle ermöglicht dann die Kommunikation der Module. Durch die Aufteilung einer Umgebung von Softwarekonstrukten in verschiedene Module können wir die Kopplung minimieren, die Anwendungsentwicklung optimieren und die Systemkomplexität reduzieren.

Die Modularität ermöglicht es Programmierern, Funktionstests isoliert durchzuführen und während eines bestimmten Sprints oder Projekts parallele Entwicklungsanstrengungen durchzuführen. Dies erhöht die Effizienz über den gesamten Lebenszyklus der Softwareentwicklung.

Einige charakteristische Attribute eines echten Moduls sind:

  • Eine autonome Einsatzeinheit (lose Kupplung)
  • Eine konsistente und eindeutige Identität (Modul-ID und Version)
  • Leicht zu identifizierende und zu erkennende Anforderungen und Abhängigkeiten (Standardfunktionen für die Kompilierung und Bereitstellung sowie Metainformationen)
  • Eine offene und verständliche Schnittstelle (Kommunikationsvertrag)
  • Versteckte Implementierungsdetails (Kapselung)

Systeme, die zur effizienten Verarbeitung von Modulen entwickelt wurden, sollten Folgendes tun:

  • Unterstützt Modularität und Abhängigkeitserkennung zur Kompilierungszeit
  • Führen Sie Module in einer Laufzeitumgebung aus, die eine einfache Bereitstellung und erneute Bereitstellung ohne Systemausfallzeiten unterstützt
  • Implementieren Sie einen klaren und robusten Ausführungslebenszyklus
  • Bieten Sie Funktionen für die einfache Registrierung und Erkennung von Modulen

Objektorientierte, komponentenorientierte und serviceorientierte Lösungen haben alle versucht, reine Modularität zu ermöglichen. Jede Lösung hat ihre eigenen Macken, die jedoch verhindern, dass sie modulare Perfektion erreicht. Lassen Sie uns kurz überlegen.

Java-Klassen und -Objekte als modulare Konstrukte

Erfüllt die objektorientierte Natur von Java nicht die Anforderungen der Modularität? Schließlich betont und erzwingt die objektorientierte Programmierung mit Java die Eindeutigkeit, Datenkapselung und lose Kopplung. Beachten Sie die Modularitätsanforderungen, die von Javas objektorientiertem Framework nicht erfüllt werden , obwohl diese Punkte ein guter Anfang sind : Identität auf Objektebene ist unzuverlässig; Schnittstellen sind nicht versioniert: und Klassen sind auf Bereitstellungsebene nicht eindeutig. Lose Kopplung ist eine bewährte Methode, wird aber sicherlich nicht durchgesetzt.

Die Wiederverwendung von Klassen in Java ist schwierig, wenn Abhängigkeiten von Drittanbietern so leicht missbraucht werden. Tools zur Kompilierungszeit wie Maven versuchen, dieses Manko zu beheben. Nachträgliche Sprachkonventionen und -konstrukte wie Abhängigkeitsinjektion und Kontrollinversion helfen Entwicklern bei unseren Versuchen, die Laufzeitumgebung zu steuern, und manchmal sind sie erfolgreich, insbesondere wenn sie mit strenger Disziplin verwendet werden. Leider überlässt diese Situation die Erstellung einer modularen Umgebung den proprietären Framework-Konventionen und -Konfigurationen.

Java fügt dem Mix außerdem Paket-Namespaces und Sichtbarkeit des Bereichs hinzu, um modulare Mechanismen zur Kompilierungs- und Bereitstellungszeit zu erstellen. Aber diese Sprachmerkmale lassen sich leicht umgehen, wie ich erklären werde.

Pakete als modulare Lösung

Pakete versuchen, der Java-Programmierlandschaft eine Abstraktionsebene hinzuzufügen. Sie bieten Funktionen für eindeutige Codierungsnamespaces und Konfigurationskontexte. Leider lassen sich Paketkonventionen leicht umgehen, was häufig zu einer Umgebung gefährlicher Kopplungen zur Kompilierungszeit führt.

Der derzeitige Stand der Modularität in Java (abgesehen von OSGi, auf das ich gleich eingehen werde) wird am häufigsten mithilfe von Paketnamespaces, JavaBeans-Konventionen und proprietären Framework-Konfigurationen wie im Frühjahr erreicht.

Sind JAR-Dateien nicht modular genug?

JAR-Dateien und die Bereitstellungsumgebung, in der sie ausgeführt werden, verbessern die vielen anderen ansonsten verfügbaren Bereitstellungskonventionen erheblich. JAR-Dateien weisen jedoch keine eindeutige Eindeutigkeit auf, abgesehen von einer selten verwendeten Versionsnummer, die in einem .jar-Manifest verborgen ist. Die JAR-Datei und das optionale Manifest werden in der Java-Laufzeitumgebung nicht als Modularitätskonventionen verwendet. Daher sind die Paketnamen der Klassen in der Datei und ihre Teilnahme an einem Klassenpfad die einzigen Teile der JAR-Struktur, die der Laufzeitumgebung Modularität verleihen.

Kurz gesagt, JARs sind ein guter Versuch zur Modularisierung, erfüllen jedoch nicht alle Anforderungen für eine wirklich modulare Umgebung. Frameworks und Plattformen wie Spring und OSGi verwenden Muster und Verbesserungen der JAR-Spezifikation, um Umgebungen für den Aufbau sehr leistungsfähiger und modularer Systeme bereitzustellen. Im Laufe der Zeit werden jedoch selbst diese Tools einem sehr unglücklichen Nebeneffekt der JAR-Spezifikation JAR Hell erliegen!

Klassenpfad / JAR Hölle

Wenn die Java-Laufzeitumgebung beliebig komplexe JAR-Lademechanismen zulässt, wissen Entwickler, dass sie sich in der Klassenpfad- oder JAR-Hölle befinden . Eine Reihe von Konfigurationen kann zu diesem Zustand führen.

Stellen Sie sich zunächst eine Situation vor, in der ein Java-Anwendungsentwickler eine aktualisierte Version der Anwendung bereitstellt und diese in eine JAR-Datei mit genau demselben Namen wie die alte Version gepackt hat. Die Java-Laufzeitumgebung bietet keine Validierungsfunktionen zum Ermitteln der richtigen JAR-Datei. Die Laufzeitumgebung lädt einfach Klassen aus der JAR-Datei, die sie zuerst findet oder die eine von vielen Klassenpfadregeln erfüllt. Dies führt bestenfalls zu unerwartetem Verhalten.

Eine weitere Instanz der JAR-Hölle entsteht, wenn zwei oder mehr Anwendungen oder Prozesse von verschiedenen Versionen einer Bibliothek eines Drittanbieters abhängen. Bei Verwendung der Standardfunktionen zum Laden von Klassen ist zur Laufzeit nur eine Version der Bibliothek eines Drittanbieters verfügbar, was zu Fehlern in mindestens einer Anwendung oder einem Prozess führt.

Ein voll funktionsfähiges und effizientes Java-Modulsystem sollte die Trennung von Code in verschiedene, leicht verständliche und lose gekoppelte Module erleichtern. Abhängigkeiten sollten klar spezifiziert und strikt durchgesetzt werden. Es sollten Einrichtungen verfügbar sein, mit denen Module aktualisiert werden können, ohne dass sich dies negativ auf andere Module auswirkt. Eine modulare Laufzeitumgebung sollte Konfigurationen ermöglichen, die für eine bestimmte Domäne oder einen bestimmten vertikalen Markt spezifisch sind, wodurch die Startzeit und der System-Footprint der Umgebung verringert werden.

Modularitätslösungen für Java

Neben den bisher erwähnten Modularitätsmerkmalen kommen in jüngster Zeit einige weitere hinzu. Die folgenden Funktionen sollen die Leistung optimieren und die Laufzeitumgebung erweitern:

  • Segmentierter Quellcode : Quellcode, der in verschiedene zwischengespeicherte Segmente unterteilt ist, von denen jedes einen bestimmten Typ von kompiliertem Code enthält. Zu den Zielen gehören das Überspringen von Nicht-Methoden-Code während Garbage Sweeps, inkrementelle Builds und eine bessere Speicherverwaltung.
  • Durchsetzungszeit erzwingen: Sprachkonstrukte zum Erzwingen von Namespaces, Versionierung, Abhängigkeiten und anderen.
  • Bereitstellungsfunktionen : Unterstützung für die Bereitstellung skalierter Laufzeitumgebungen gemäß spezifischen Anforderungen, z. B. einer mobilen Geräteumgebung.

Eine Reihe von Modularitätsspezifikationen und Frameworks haben versucht, diese Funktionen zu vereinfachen, und einige sind kürzlich in Vorschlägen für Java 9 an die Spitze gerückt. Eine Übersicht über Vorschläge zur Java-Modularität finden Sie weiter unten.

JSR (Java Specification Request) 277

Derzeit inaktiv ist Java Specification Request (JSR) 277, das Java-Modulsystem. eingeführt von Sun im Juni 2005. Diese Spezifikation deckte die meisten Bereiche von OSGi ab. Wie OSGi definiert auch JSR 277 die Erkennung, das Laden und die Konsistenz von Modulen, wobei Laufzeitänderungen und / oder Integritätsprüfungen nur spärlich unterstützt werden.

Zu den Nachteilen von JSR 277 gehören:

  • Kein dynamisches Laden und Entladen von Modulen / Bundles
  • Keine Laufzeitprüfungen auf Eindeutigkeit des Klassenraums

OSGi (Open Service Gateway Initiative)

Die OSGI-Plattform wurde im November 1998 von der OSGI Alliance eingeführt und ist die am häufigsten verwendete Modularitätsantwort auf die formale Standardfrage für Java. Derzeit in Version 6 ist die OSGi-Spezifikation weit verbreitet und wird insbesondere in letzter Zeit verwendet.

Im Wesentlichen ist OSGi ein modulares System und eine Serviceplattform für die Programmiersprache Java, die ein vollständiges und dynamisches Komponentenmodell in Form von Modulen, Services, implementierbaren Bundles usw. implementiert.

Die primären Schichten der OSGI-Architektur sind wie folgt:

  • Ausführungsumgebung : Die Java-Umgebung (z. B. Java EE oder Java SE), unter der ein Bundle ausgeführt wird.
  • Modul : Wo das OSGi-Framework die modularen Aspekte eines Bundles verarbeitet. Bundle-Metadaten werden hier verarbeitet.
  • Lebenszyklus : Das Initialisieren, Starten und Stoppen von Bundles erfolgt hier.
  • Dienstregistrierung : Hier listen Bundles ihre Dienste auf, damit andere Bundles sie entdecken können.

Einer der größten Nachteile von OSGi ist das Fehlen eines formalen Mechanismus für die native Paketinstallation.

JSR 291

JSR 291 ist ein dynamisches Komponentenframework für Java SE, das auf OSGi basiert und sich derzeit in der Endphase der Entwicklung befindet. Diese Bemühungen konzentrieren sich darauf, OSGi in Mainstream-Java zu integrieren, wie dies für die mobile Java-Umgebung von JSR 232 durchgeführt wurde.

JSR 294

JSR 294 definiert ein System von Metamodulen und delegiert die tatsächliche Ausführungsform steckbarer Module (Versionen, Abhängigkeiten, Einschränkungen usw.) an externe Anbieter. Diese Spezifikation führt Spracherweiterungen wie "Superpackages" und hierarchisch verwandte Module ein, um die Modularität zu erleichtern. Strenge Kapselung und unterschiedliche Kompilierungseinheiten stehen ebenfalls im Fokus der Spezifikation. JSR 294 ist derzeit inaktiv.

Projekt Puzzle

Project Jigsaw ist der wahrscheinlichste Kandidat für Modularität in Java 9. Jigsaw versucht, mithilfe von Sprachkonstrukten und Umgebungskonfigurationen ein skalierbares Modulsystem für Java SE zu definieren. Hauptziele von Jigsaw sind:

  • Dies macht es sehr einfach, die Java SE-Laufzeit und das JDK auf kleine Geräte zu skalieren.
  • Verbesserung der Sicherheit von Java SE und des JDK durch Verbot des Zugriffs auf interne JDK-APIs sowie durch Erzwingen und Verbessern der SecurityManager.checkPackageAccessMethode.
  • Verbesserung der Anwendungsleistung durch Optimierung des vorhandenen Codes und Erleichterung von Vorausschau-Programmoptimierungstechniken.
  • Vereinfachung der Anwendungsentwicklung in Java SE, indem Bibliotheken und Anwendungen aus von Entwicklern bereitgestellten Modulen und aus einem modularen JDK erstellt werden können
  • Erfordern und Durchsetzen eines endlichen Satzes von Versionsbeschränkungen

JEP (Java Enhancement Proposal) 200

Mit dem im Juli 2014 erstellten Java Enhancement Proposal 200 soll eine modulare Struktur für das JDK definiert werden. JEP 200 baut auf dem Jigsaw-Framework auf, um die Segmentierung des JDK gemäß Java 8 Compact Profiles in Sätze von Modulen zu erleichtern, die zur Kompilierungs-, Erstellungs- und Bereitstellungszeit kombiniert werden können. Diese Kombinationen von Modulen können dann als verkleinerte Laufzeitumgebungen bereitgestellt werden, die aus Jigsaw-kompatiblen Modulen bestehen.

JEP 201

JEP 201 versucht, auf Jigsaw aufzubauen, um den JDK-Quellcode in Module umzuwandeln. Diese Module können dann von einem erweiterten Build-System, das Modulgrenzen erzwingt, als separate Einheiten kompiliert werden. JEP 201 schlägt ein Quellcode-Restrukturierungsschema im gesamten JDK vor, das die Modulgrenzen auf der obersten Ebene der Quellcodebäume hervorhebt.

Penrose

Penrose würde die Interoperabilität zwischen Jigsaw und OSGi verwalten. Insbesondere würde es die Möglichkeit erleichtern, OSGi-Mikrokerne zu modifizieren, damit Bündel, die im modifizierten Kernel ausgeführt werden, Jigsaw-Module verwenden können. Es basiert auf der Verwendung von JSON zur Beschreibung von Modulen.

Pläne für Java 9

Java 9 ist eine einzigartige Hauptversion für Java. Was es einzigartig macht, ist die Einführung modularer Komponenten und Segmente im gesamten JDK . Die Hauptmerkmale, die die Modularisierung unterstützen, sind:

  • Modularer Quellcode : In Java 9 werden JRE und JDK in interoperable Module umgewandelt. Dies ermöglicht die Erstellung skalierbarer Laufzeiten, die auf kleinen Geräten ausgeführt werden können.
  • Segmentierter Code-Cache : Der neue segmentierte Code-Cache von Java 9 ist zwar keine rein modulare Funktion, folgt jedoch dem Geist der Modularisierung und bietet einige der gleichen Vorteile. Der neue Code-Cache trifft intelligente Entscheidungen, um häufig aufgerufene Codesegmente zu nativem Code zu kompilieren und für eine optimierte Suche und zukünftige Ausführung zu speichern. Der Heap wird auch in drei verschiedene Einheiten unterteilt: Nicht-Methoden-Code, der permanent im Cache gespeichert wird; Code mit einem möglicherweise langen Lebenszyklus (als "nicht profilierter Code" bezeichnet); und Code, der vorübergehend ist (bekannt als "profilierter Code").
  • Durchsetzungszeit für die Erstellung: Das Erstellungssystem wird über JEP 201 erweitert, um Modulgrenzen zu kompilieren und durchzusetzen.
  • Bereitstellungsfunktionen : Im Rahmen des Jigsaw-Projekts werden Tools bereitgestellt, die Modulgrenzen, Einschränkungen und Abhängigkeiten zur Bereitstellungszeit unterstützen.

Java 9 Early Access Release

Während das genaue Veröffentlichungsdatum von Java 9 ein Rätsel bleibt, können Sie eine frühzeitige Zugriffsversion auf Java.net herunterladen.

Abschließend

Dieser Artikel gab einen Überblick über die Modularität innerhalb der Java-Plattform, einschließlich der Aussichten auf Modularität in Java 9. Ich erklärte, wie langjährige Probleme wie die Klassenpfad-Hölle zur Notwendigkeit einer modulareren Java-Architektur beitragen, und erörterte einige der neuesten neuen Modularitäten Für Java vorgeschlagene Funktionen. Anschließend habe ich alle Vorschläge oder Plattformen für Java-Modularität beschrieben und kontextualisiert, einschließlich OSGi und Project Jigsaw.

Die Notwendigkeit einer modulareren Java-Architektur ist klar. Aktuelle Versuche sind gescheitert, obwohl OSGi sehr nahe kommt. Für die Java 9-Version werden Project Jigsaw und OSGi die Hauptakteure im modularen Bereich für Java sein, wobei Penrose möglicherweise den Klebstoff zwischen ihnen bereitstellt.

Diese Geschichte "Modularität in Java 9: ​​Stapeln mit Project Jigsaw, Penrose und OSGi" wurde ursprünglich von JavaWorld veröffentlicht.