Java 9 und das Modulsystem: Was Entwickler wissen müssen
Zurück zum Blog

Java 9 und das Modulsystem: Was Entwickler wissen müssen

6 Min. Lesezeit
Lesen auf English

Java 9 und das Modulsystem: Was Entwickler wissen müssen

Seit dem 21. September ist es offiziell: Java 9 ist da. Nach mehrfachen Verzögerungen -- ursprünglich war der Release für 2016 geplant -- ist das wohl größte Update der Java-Plattform seit der Einführung von Generics endlich verfügbar. Das Herzstück: das Java Platform Module System (JPMS), besser bekannt unter dem Projektnamen Jigsaw.

In diesem Artikel schauen wir uns an, was das Modulsystem konkret bedeutet, wie die neue module-info.java funktioniert und worauf man bei der Migration bestehender Projekte achten sollte.

Was ist Project Jigsaw?

Das Modulsystem löst ein Problem, das Java seit seinen Anfängen begleitet: die fehlende Kapselung auf Paketebene. Bisher konnte jede Klasse, die als public deklariert war, von überall im Classpath verwendet werden -- auch wenn sie eigentlich als internes Implementierungsdetail gedacht war. Wer hat nicht schon einmal Klassen aus sun.misc.* oder com.sun.* verwendet, obwohl diese nie als öffentliche API vorgesehen waren?

JPMS führt eine neue Ebene oberhalb von Paketen ein: Module. Ein Modul definiert explizit:

  • Welche Pakete es nach außen sichtbar macht (exports)
  • Welche anderen Module es benötigt (requires)
  • Welche Services es bereitstellt oder konsumiert

Damit bekommt Java erstmals echte Kapselung auf Architekturebene. Die JDK-Bibliothek selbst wurde in rund 90 Module aufgeteilt -- von java.base (das jedes Modul automatisch importiert) über java.sql bis hin zu java.logging.

Die module-info.java im Detail

Jedes Modul wird durch eine Datei module-info.java im Wurzelverzeichnis des Source-Trees beschrieben. Die Syntax ist schlank und gut lesbar:

module com.encircle360.backend {
    // Abhängigkeiten deklarieren
    requires java.sql;
    requires java.logging;
    requires spring.core;
    requires spring.context;

    // Pakete nach außen freigeben
    exports com.encircle360.backend.api;
    exports com.encircle360.backend.model;

    // Interne Pakete bleiben gekapselt -- kein exports nötig
    // com.encircle360.backend.internal ist von außen nicht sichtbar

    // Reflection gezielt erlauben (z.B. für Frameworks)
    opens com.encircle360.backend.model to spring.core;
}

Die wichtigsten Schlüsselwörter im Überblick:

  • requires -- Deklariert eine Abhängigkeit zu einem anderen Modul. Mit requires transitive wird die Abhängigkeit an konsumierende Module weitergereicht.
  • exports -- Macht ein Paket für andere Module sichtbar. Nur exportierte Pakete sind von außen zugreifbar.
  • opens -- Erlaubt Reflection-Zugriff auf ein Paket. Das ist besonders für Frameworks wie Spring oder Hibernate relevant, die intensiv mit Reflection arbeiten.
  • provides ... with -- Registriert eine Service-Implementierung für das ServiceLoader-API.
  • uses -- Deklariert, dass das Modul einen bestimmten Service konsumiert.

Was ändert sich für bestehende Projekte?

Die gute Nachricht zuerst: Java 9 ist weitgehend abwärtskompatibel. Bestehender Code, der keine internen JDK-APIs verwendet, sollte in den meisten Fällen ohne Änderungen auf Java 9 laufen. Der sogenannte Unnamed Module sorgt dafür, dass Code ohne module-info.java weiterhin funktioniert -- alles auf dem Classpath landet automatisch in diesem Modul und hat Zugriff auf alle exportierten Pakete.

Die weniger gute Nachricht: Wer interne JDK-APIs verwendet hat, wird Probleme bekommen. sun.misc.Unsafe, sun.misc.BASE64Encoder und ähnliche Klassen sind jetzt gekapselt. Die JVM gibt zwar mit --illegal-access=warn standardmäßig noch eine Warnung statt eines Fehlers aus, aber das ist als Übergangslösung gedacht. In zukünftigen Java-Versionen wird der Zugriff vollständig blockiert.

Typische Stolpersteine bei der Migration

Split Packages: Wenn zwei JARs Klassen im selben Paket enthalten, funktioniert das im Modulsystem nicht mehr. Jedes Paket darf nur einem Modul zugeordnet sein. In der Praxis betrifft das vor allem Projekte, die ältere Bibliotheken mit überlappenden Paketnamen verwenden.

Reflection-Zugriffe: Frameworks wie Spring, Hibernate oder Jackson setzen massiv auf Reflection. Im Modulsystem muss Reflection explizit über opens erlaubt werden. Wer ein ganzes Modul für Reflection öffnen möchte, kann open module verwenden -- das ist pragmatisch, aber natürlich ein Kompromiss bei der Kapselung.

Tooling und Build-Systeme: Maven und Gradle unterstützen Java 9 Module bereits grundlegend, aber die Integration ist noch nicht überall ausgereift. Gerade bei Multi-Module-Projekten mit Maven kann die Konfiguration des Module-Paths knifflig werden.

Ehrliche Einschätzung: Sollte man jetzt migrieren?

Stand heute -- wenige Tage nach dem Release -- ist unsere Empfehlung: Noch nicht überstürzen. Viele populäre Bibliotheken und Frameworks bieten noch keine vollständige JPMS-Unterstützung. Spring Framework 5, das Java 9 offiziell unterstützen wird, ist noch nicht final released. Hibernate, Jackson, Guava und viele weitere Bibliotheken arbeiten ebenfalls noch an der Modularisierung.

Für neue Greenfield-Projekte kann man Java 9 als Runtime nutzen und schrittweise Module einführen. Für bestehende Projekte empfehlen wir:

  1. Zunächst nur die Runtime aktualisieren -- Java 9 als Laufzeitumgebung verwenden, ohne eigene Module zu definieren
  2. Abhängigkeiten prüfen -- mit jdeps analysieren, welche internen APIs verwendet werden
  3. Interne API-Nutzung eliminieren -- sun.misc.BASE64Encoder durch java.util.Base64 ersetzen und ähnliche Aufräumarbeiten durchführen
  4. Module schrittweise einführen -- wenn alle Abhängigkeiten bereit sind

Weitere Highlights in Java 9

Neben dem Modulsystem bringt Java 9 einige weitere interessante Features:

JShell (REPL): Java hat endlich eine interaktive Shell. Mit jshell kann man Java-Code direkt in der Kommandozeile ausprobieren -- ideal für schnelles Prototyping und zum Lernen.

Private Methoden in Interfaces: Seit Java 8 können Interfaces Default-Methoden haben. Java 9 erlaubt nun auch private Methoden in Interfaces, was die Wiederverwendung von Code innerhalb von Interface-Definitionen verbessert.

Verbesserungen an der Stream API: Neue Methoden wie takeWhile(), dropWhile() und ofNullable() machen die Arbeit mit Streams flexibler.

HTTP/2 Client (Incubator): Ein neuer HTTP-Client mit Unterstützung für HTTP/2 und WebSockets ist als Incubator-Modul enthalten. Er soll langfristig den in die Jahre gekommenen HttpURLConnection ersetzen.

Collection Factory Methods: Immutable Collections lassen sich jetzt elegant erstellen: List.of("a", "b", "c"), Set.of(1, 2, 3) und Map.of("key", "value").

Fazit

Java 9 ist ein bedeutender Release, und das Modulsystem wird die Art, wie wir Java-Anwendungen strukturieren, langfristig verändern. Die Vorteile -- echte Kapselung, klare Abhängigkeiten, kleinere Runtime-Images durch jlink -- sind real und relevant.

Gleichzeitig ist die Migration kein Selbstläufer. Das Ökosystem braucht Zeit, um nachzuziehen. Wer jetzt schon Java 9 evaluiert und seine Codebasis vorbereitet, wird den Übergang deutlich leichter schaffen, wenn die Bibliothekslandschaft bereit ist.

Wir bei encircle360 begleiten diesen Übergang aktiv und werden in kommenden Artikeln über unsere Erfahrungen mit der Migration konkreter Projekte berichten. Bei Fragen zur Java-9-Migration stehen wir gerne zur Verfügung.

Teilen

Patrick Hütter

Geschrieben von

Patrick Hütter

Gründer & Software-Architekt

Softwarearchitekt, Ingenieur und Unternehmer. Patrick baut seit über einem Jahrzehnt Produkte und Plattformen — von Enterprise-Backends über Cloud-Native-Infrastruktur bis hin zu KI-gestützten Anwendungen. Als Gründer von encircle360 verbindet er technische Tiefe mit unternehmerischem Denken und treibt Open-Source-Projekte voran, die echten Impact haben.