JDK9 bringt uns das neue Modulsystem Jigsaw (englisch, u.a. für Puzzle). Wir haben jetzt Module in Java 9. Doch was heißt das konkret?
In einigen Artikeln auf diesem Blog soll Jigsaw näher erläutert werden. In diesem Artikel wird der Aspekt des Exportierens von Java-Funktionalität aus Modulen vorgestellt.
- Teil 1: Modul-Deklaration und Modul-Abhängigkeiten
- Teil 2: Exportieren von Java-Packages und Interfaces aus Modulen
- Teil 3: Modulkategorien und Umgang mit Legacy-Code (Java ≤ 8)
Exportieren von Java-Packages
Um Funktionalität zur Verfügung zu stellen, muss ein Modul Java-Packages exportieren. In Listing 1 werden die beiden Packages de.holisticon.servlet4demo und de.holisticon.servlet4demo.util exportiert.
Hier kann nicht mit sogenannten Wildcards à la „.*“ gearbeitet werden. Jedes einzelne exportierte Package muss explizit mit einem eigenen exports-Statement angegeben werden.
Damit ist ein Package für alle anderen Module sichtbar. Dies kann nun noch ein wenig eingeschränkt werden. Mithilfe einer exports package to other module kann ein Package ausdrücklich für ein bestimmtes Modul exportiert werden. Dann kann dieses Package ausschließlich von dem genannten Modul verwendet werden.
/** * Modul mit dem Namen * de.holisticon.servlet4demospringboot */ module de.holisticon.servlet4demospringboot { // Exportierte Packages aus diesem Modul exports de.holisticon.servlet4demo; exports de.holisticon.servlet4demo.util; // Export explizit für ein bestimmtes // anderes Modul exports de.holisticon.servlet4demo.request to de.holisticon.http2demo // Benötigte Module ... requires spring.core; // etc ... }
Listing 1: Eine simple Moduldefinition
Split-Package-Problem
Module müssen disjunkte Packages haben. Zwei oder mehr Module dürfen also kein Package gleichen Namens enthalten. Ein solches Split Package auf zwei oder mehr Module führt zu einem Fehler beim Starten der JVM.
Modulbeziehungen mit uses und provides
Module können Beziehungen vom Typ uses <Java Interface> und provides <Java Interface> definieren.
Damit drücken sie aus, dass sie ein bestimmtes Java Interface benötigen oder anbieten. Im unten gezeigten Beispiel nutzt das Modul java.sql das Modul com.mysql.jdbc, um eine Implementierung des Java-Interfaces java.sql.Driver zu erhalten.
Die Beziehung zwischen den Modulen besteht dabei nur indirekt. java.sql nennt com.mysql.jdbc nicht explizit als Abhängigkeit. Die Abhängigkeit wird über das Java Interface aufgelöst.
java.sql benötigt eine Implementierung für das Java-Interface java.sql.Driver, und com.mysql.jdbc bietet eine entsprechende Implementierung über die Klasse com.mysql.jdbc.Driver an.
Bereits beim Starten der JVM muss jedes uses durch genau ein passendes provides aufgelöst werden. Andernfalls startet die JVM nicht.
Damit soll schon zur JVM-Bootzeit sichergestellt werden, dass Nutzungsbeziehungen zwischen Modulen erfüllt sind.
module java.sql { // Definiert Schnittstelle und exportiert sie uses java.sql.Driver; exports java.sql; } module com.mysql.jdbc { requires java.sql; // Implementiert Schnittstelle? provides java.sql.Driver with com.mysql.jdbc.Driver; }
Ausblick — was bringt das JDK9 sonst noch?
Im nächsten Artikel wird der Aspekt des Umgangs mit nicht-modularisiertem Code aus Java 8 (oder älter) in Java-9-Projekten besprochen.
Bildhinweis
Bild mit Puzzle-Teilen von Lilla Frerichs unter Public Domain Dedication Lizenz.