Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Effective Java

Effective Java
Überblick über Java 9
Teil 4: JPMS - Java Platform Module System
 

Java Magazin, Januar 2017
Klaus Kreft & Angelika Langer

Dies ist die Überarbeitung eines Manuskripts für einen Artikel, der im Rahmen einer Kolumne mit dem Titel "Effective Java" im Java Magazin erschienen ist.  Die übrigen Artikel dieser Serie sind ebenfalls verfügbar ( click here ).

 

Project Jigsaw aka JPMS (Java Platform Module System)

Mit dem Release 9 bekommt Java nun endlich das schon lange angekündigte Modul-System (siehe / MOSY /).  Oracle hat bereits vor ca. 5 Jahren damit begonnen, die Modularisierung von Java-Anwendungen zu unterstützen. In diesem Zuge wurde zunächst einmal der JDK selbst restrukturiert und in Module aufgeteilt.  Die Modularisierung des JDK ist nun fertig und damit hat sich das Deployment des JDK geändert: es gibt in Java 9 nun kein  rt.jar mehr, sondern der JDK besteht jetzt aus Modulen.  Diesem Beispiel soll die Java--Community folgen.  Die Idee ist, dass mit Java 9 eine Transitionsphase beginnt, in der insbesondere Third-Party-Provider und schließlich alle Java-Anwendungen ihre Java-Software modularisieren.

Was ist ein Modul?

Module sind Artefakte, die eine Einheit von miteinander zusammenhängenden Klassen, native Code, Properties, etc. bilden.  Zu jedem Modul gehören Metadaten, die den Modul und seine Beziehungen zu anderen Modulen beschreiben.  Das Hauptziel der Modularisierung ist die Kapselung von zusammenhängenden Klassen und die saubere Beschreibung der Beziehungen zwischen den Modulen. 

 
 

Ein Modul in Java beschreibt:

- Readability :               welche anderen Module dieser Modul benutzt

- Accessibility :              welche Packages dieser Modul anderen Modulen zur Verfügung stellt.

Der Zweck des Modul-Systems ist es, diese Beziehungen der Module untereinander von der Übersetzung bis zur Laufzeit zu überprüfen und sicher zu stellen.

Wozu braucht Java ein Modul-System? 

Die Möglichkeit, zusammenhängende Klassen zusammen zu fassen, gibt es bereits mit dem Java Archiven, d.h. den  .jar -Dateien.  Außerdem gibt es in Java Kapselungsmechanismen mit Hilfe von Packages und den Visibility-Deklarationen über  private protected und  public .  Wozu also braucht es Modul-System für Java?  Schauen wir uns einige der Gründe an, die dazu geführt haben, dass ein Modul-System für Java definiert wurde.

 
 

Die Visibility-Deklarationen beispielsweise haben sich als unzureichend heraus gestellt.  Ein gutes Beispiel für die Defizite sind die JDK-Klassen in den  sun.* -Packages wie zum Beispiel die Klasse  sun.misc.Unsafe .  Alle Klassen in den  sun.* -Packages waren ursprünglich als interne Klassen für die Implementierung des JDK gedacht und sollten auf keinen Fall allen beliebigen Java-Benutzern zur Verfügung stehen.  Solche internen Klassen deklariert man mit den traditionellen Mitteln als package-visible und legt sie zusammen mit den Klassen, die sie benutzen dürfen, in ein gemeinsames Package.  Nun werden die internen  sun.*- Klassen aber von so vielen JDK-Klassen verwendet, dass man riesengroße Packages bekommen hätte.  Das wollte man natürlich auch nicht.  Also hat man Klassen wie  sun.misc.Unsafe notgedrungen  public gemacht und sich andere Schutzmechanismen überlegt, um sie der allgemeinen Benutzung zu entziehen.  Aber all diese Schutzmechanismen lassen sich aushebeln, so dass Klassen wie  sun.misc.Undafe heute de facto keine internen JDK-Klassen mehr sind, sondern in allen möglichen Anwendungen und Bibliotheken benutzt werden, die sie eigentlich gar nicht benutzen dürfen.  Mit anderen Worten, die traditionellen Kapselungsmechanismen bieten keinen ausreichenden Schutz.  Das wird sich mit dem Modul-System ändern.
 
 

Ein anderes Problem mit den traditionellen Mechanismen ist die sogenannte " jar hell ".  In größeren Applikationen kommt es immer wieder vor, dass eine bestimmte Third-Party-Bibliothek von verschiedenen Teilen der Applikation in verschiedenen Versionen benötigt wird.  Nun kann man zwar die jar-Dateien für die beiden Versionen der Bibliothek auf den Classpath legen, aber es wird beim Classloading später die Klasse aus der jar-Datei genommen, die als erste auf dem Classpath gefunden wurde.  Es ist mit den traditionellen Mitteln nicht möglich zu spezifizieren, welche Komponenten in welcher Version von wem gebraucht werden.  Auch das wird sich mit den Modulen ändern.
 
 

Ein anderes Problem ist die Größe der Anwendungen.  Der JDK zum Beispiel ist von Release zu Release immer größer geworden   Mit den traditionellen Mechanismen gibt es jedoch keine Möglichkeit, partielle JDK-Deployments zu machen. 
 
 

Diese und andere Gründe haben dazu geführt, dass ein Modul-System für Java mit besseren Kapselungsmechnismen für erforderlich gehalten wurde.

Der modularisierte JDK

Als erstes hat man bei Oracle den JDK reorganisiert und in Module zerlegt.  Dafür hat man ca. 5 Jahre gebraucht und der Graph der Modulbeziehungen sieht in vereinfachter Form nun so aus, wie unten gezeigt.  Die tatsächlichen Beziehungen sind noch deutlich komplexer; man kann sich das komplette Diagramm im JEP 200: "The Modular JDK" anschauen (siehe / MJDK /).

 
 

                                                                           Copyright © 2015, Oracle and/or its affiliates.  All rights reserved.

Abb. 1: Ausschnitt aus den Plattform-Modulen des JDK 9
 
 

Welche Auswirkungen hat die Modularisierung des JDK für die Java-Entwickler?  Im günstigsten Falle merken wir als Entwickler fast gar nichts davon und unsere Anwendung läuft mit dem modularisierten JDK 9 genauso wie mit dem alten JDK 8. 
 
 

Es kann aber auch sein, dass beim Umstieg auf Java 9 Anpassungen an der eigenen Applikation gemacht werden müssen.  Das ist beispielsweise in folgenden Fällen nötig:

- Die Anwendung verwendet JDK-interne APIs aus den  sun.* -Packages.  Diese internen Klassen sind im modularisierten JDK nun wirklich intern und nicht mehr zugänglich.

- Die Anwendung ist in irgendeiner Form abhängig vom Deployment des JDK oder des JRE, z.B. wenn direkt auf die  rt.jar -Datei zugegriffen wird.   Diese Datei gibt es nicht mehr.

- Die Anwendung wertet den Java-Version-String aus.  Das wird wahrscheinlich nicht mehr funktionieren, denn die Syntax der Versionsbezeichnungen hat sich geändert.

- Die Anwendung verwendet sogenannte " Split Packages ".  Das kommt vor, wenn die Anwendung APIs benutzt, die in einer Third-Party-Software nicht  public , sondern nur package-visible sind.  Um an die package-visible APIs heran zu kommen, muss man die benutzenden eigenen Klassen in dem betreffenden fremden Package definieren.   So etwas geht mit Modulen nicht mehr.

- usw. 

Die Liste ist nicht vollständig.  Es gibt noch mehr Szenarien, in denen Anpassungen der Applikation an den modularisierten JDK 9 erforderlich sein können.

Abhängigkeiten zu JDK-internen APIs aus den sun.*-Packages

Ob die eigene Anwendung interne APIs verwendet, kann man mit dem Java Class Dependency Analyzer ü berprüfen.  Dieses  jdeps -Werkzeug gibt es schon seit Java 8 als Hilfe bei der Modularisierung der eigenen Anwendungen: es analysiert die Abhängigkeiten der Packages voneinander.  Wenn man das  jdeps -Tool mit der Option  -jdkinternals aufruft, dann sucht es auch nach der Verwendung von JDK-internen APIs und meldet sie.   Die Meldung sieht dann z.B. so aus:
        ......
     -> sun.security.x509.X500Name JDK internal API (java.base)
Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.

Please modify your code to eliminate dependency on any JDK internal APIs.

For the most recent update on JDK internal API replacements, please check:

https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
 
 

JDK Internal API              Suggested Replacement

----------------              ---------------------

sun.security.x509.X500Name    Use javax.security.auth.x500.X500Principal @since 1.4
 
 

Man bekommt auch gleich Hinweise auf mögliche alternative APIs, die man anstelle der internen JDK APIs verwenden soll. Wenn man solche Verwendungen im eigenen Code findet, dann soll man die betreffenden Stellen entsprechend ändern, damit die Anwendung mit dem JDK 9 funktioniert.
 
 

Nun kann es aber auch passieren, dass nicht der eigene Code die internen JDK APIs verwendet, sondern dass benutzte Third-Party-Komponenten solche Abhängigkeiten aufweisen.  Den fremden Code kann und will man nicht ändern.  Prinzipiell sollen die Bibliotheks- und Framework-Hersteller sukzessive ihre Komponenten modularisieren und dabei die Abhängigkeiten zu irgendwelchen JDK internen APIs eliminieren.  Aber was macht man in der Übergangszeit?
 
 

Für diese Übergangszeit gibt es einen Workaround, der die Kapselungsmechnismen des Modul-Systems teilweise aushebelt: den sogenannten  Unnamed Module .  In diesem unbenannten Modul landen alle Typen, die vom Classpath geholt werden.  Dort befinden sich in Java 9 alte, nicht-modularisierte Komponenten, wie z.B. die fragliche Third-Party-Komponente mit ihrer Abhängigkeit zu den internen JDK APIs.  Für bereits modularisierte Komponenten gibt es nämlich in Java 9 einen separaten Modulepath. Module - wie z.B. diejenigen aus dem mdularen JDK 9 - werden auf dem Modulepath und nicht auf dem Classpath gesucht. 
 
 

Nehmen wir als Beispiel an, die alte, noch nicht modularisierte Third-Party-Komponente verwendet die Klasse  X500Name aus dem Package  sun.security.x509 , so wie es das  jdeps -Tool in dem Beispiel-Output oben gemeldet hat.  Dieses Package gehört im JDK 9 zum Modul  java.base und dieser Modul exportiert das Package  sun.security.x509 nicht; es ist also nicht nach außen sichtbar.  Die Verwendung der Third-Party-Komponente führt deshalb sowohl beim Übersetzen als auch beim Ablauf zu Fehlern.  Mit der  --add-exports Option kann man das interne Package  sun.security.x509 aus dem JDK-Modul java.base in den unbenannten Modul hinein exportieren:
 
 

...... --add-exports java.base/sun.security.x509=ALL-UNNAMED ...    
 
 

Diese Option muss sowohl dem  javac -Compiler als auch der JVM bei Aufruf mitgegeben werden. Damit wird das interne Package sun.security.x509 für alle Klassen im unbenannten Modul zugänglich.   Da die fragliche Third-Party-Komponente im unbenannten Modul landet, hat sie dann den gewünschten Zugriff auf das Package  sun.security.x509 .
 
 

Dieses Vorgehen ist aber wirklich nur ein Workaround und keine langfristige Lösung, denn es ist nicht garantiert, dass es das Package  sun.security.x509 im nächsten JDK überhaupt noch geben wird.  Wenn es gar nicht mehr existiert, dann kann es auch nicht mehr in den Unnamed Module exportiert werden.  Außerdem funktioniert der Workaround auch nur so lange, wie die Third-Party-Komponente über den Classpath geladen wird.  Sobald sie modularisiert ist, wird sie vom Modulepath geladen.   Dann besteht sie aus Modulen mit Namen und solche "richtigen" Module können nichts aus dem Unnamed Module verwenden.

Wie definiert man einen Modul?

Nehmen wir an, wir wollen unsere Applikation modularisieren.  Zunächst müssen wir überlegen, wie wir die Packages unserer Applikation zu Modulen zusammenfassen wollen.  Anschließend müssen wir die Module beschreiben und sie von der Übersetzung bis zum Ablauf verwenden.

 
 

Für den Source-Code bedeutet die  Modularisierung zunächst einmal, dass es oberhalb von den Package-Directories ein Modul-Directory gibt, in dem alle Packages und Source-Dateien liegen, die zu dem betreffenden Modul gehören.  In diesem Modul-Directory liegt zusätzlich eine Datei mit dem Namen  module-info.java .  Sie enthält den sogenannten Modul-Deskriptor .  Hier ein Beispiel für die Directory-Struktur der Sourcen:
 
 

src\myapp.mymodule\

          module-info.java

          de\mycompany\mypackage1\ClassA.java

          ...

          de\mycompany\mypackage2\ClassB.java

          ...
 
 

Der Modul-Deskriptor wird zusammen mit allen anderen  .java -Dateien vom Compiler übersetzt und es gibt nach der Übersetzung eine Datei Namen  module-info. class .  Der Modul-Deskriptor spezifiziert u. a. den Namen des Moduls. Zum Beispiel könnte in der  Datei  src\myapp.mymodule\module-info.java stehen:
 
 

module  myapp.mymodule {

   …

}
 
 

Der Modulname  myapp.mymodule muss mit dem Namen des Modul-Directory übereinstimmen, so wie es auch bei den Packages ist.  Desweiteren enthält der Modul-Deskriptor  export s -Anweisungen, in denen der Modul die Accessibility spezifiziert, d.h. welche Packages er anderen Modulen zur Verfügung stellt.  Hier ist ein Beispiel:
 
 

module myapp.mymodule {

    exports de.mycompany.mypackage1;

   ...

    exports de.mycompany.mypackage2  to myapp.myabmodule;

   …

}
 
 

Die erste  export s -Anweisung macht das genannte Package allen anderen Modulen zugänglich, die es benötigen.  Die zweite, qualifizierte  export s -Anweisung ist eine qualifizierte  exports -Anweisung : sie macht das betreffende Package nur einem einzigen Modul  myapp.myabmodule zugänglich.
 
 

Die Module, an die exportiert wurde, können die exportierten Packages aber nur benutzen, wenn sie ihrerseits korrespondierende  requires -Anweisungen in ihrem Modul-Deskriptor haben. Das sind die Readabilty-Spezifikationen des Moduls. Hier ist zum Beispiel der Deskritptor des Moduls  myapp.myabmodule :

Last Update:  16.08.2017 09:29:34                Page  23

Copyright @ 2017 by Angelika Langer & Klaus Kreft.  All rights reserved.  
 

Jav a 9 -  Überblick                               Klaus Kreft & Angelika Langer

 
 
 
 
 
 
 
 

module myapp.myabmodule {

   ...

    requires myapp.mymodule;

   ...

}

Abb. 2: Accessibility / Readability - Beziehung

Last Update:  16.08.2017 09:29:34                Page  23

Copyright @ 2017 by Angelika Langer & Klaus Kreft.  All rights reserved.  

Jav a 9 -  Überblick                               Klaus Kreft & Angelika Langer

 
 
 
 
 
 
 
 

Man beachte, dass  requires und  exports asymmetrisch sind:  es werden Packages exportiert , aber ganze Module importiert
 
 

Abb. 3: Accessibility / Readability - Beziehung im Detail
 
 

Diese in den Modul--Deskriptoren spezifizierten  export s - und  requires -Beziehungen werden sowohl bei der Übersetzung als auch zur Laufzeit (beim Classloading, bei der Reflection, etc.) überprüft, so dass Klassen aus dem Module  myapp.myabmodule nur dann Klassen aus dem Module  myapp.mymodule verwenden können, wenn die  export s - und  requires -Spezifikationen zusammen passen.
 
 

In der Realität wird es nicht nur bilaterale Beziehungen zwischen genau zwei beteiligten Modulen geben, sondern es werden deutlich mehr Module vorkommen.  Betrachten wir deshalb z.B. einen dritten Modul  myapp.myxymodule , der von unserem  myapp.mymodule verwendet wird.
 
 

              Abb. 4: Transitive Readability
 
 

Nehmen wir mal an, der dritte Modul exportiert an den zweiten Modul ein Package  de.mycompany.myxypackage . Höchstwahrscheinlich werden Typen aus dem Package des dritten Moduls im public API des zweiten Moduls vorkommen, wie z.B. hier im Returntyp einer Methode:
 
 

package de.mycompany.mypackage2;
 
 

public MyClass {

    public  de.mycompany.myxypackage.XyClass method() {

       …

   }

}
 
 

Der erste Modul  myapp.my abmodule verwendet die APIs aus dem zweiten Modul  myapp.mymodule und die APIs des zweiten Moduls benutzen Typen aus dem dritten Modul  mayapp.myxymodule .  Folglich benötigt nun auch der erste Modul den dritten Modul.  Damit der erste Modul nicht den dritten - und womöglich noch weitere Module - in seinem Modul-Deskriptor auflisten muss, gibt es eine transitive  requires -Anweisung .  Die Modul-Deskriptoren der drei Module sehen dann so aus:
 
 

module myapp.myabmodule {

   requires myapp.mymodule;

}
 
 

module myapp.mymodule {

   exports de.mycompany.mypackage1;

   requires  transitive myapp.myxymodule;

}
 
 

module myapp.myxymodule {

   exports de.mycompany.myxypackage;

}
 
 

Der zweite Modul verwendet den dritten Modul  per " requires transitive " und damit braucht der erste Modul den dritten Modul nicht in seinen  requires -Anweisungen nennen.

Modulare Anwendungen übersetzen und ausführen

Die Modularisierung der Applikation hat Auswirkungen auf den gesamten Entwicklungsprozess von der Compilierung bis zum Ablauf. Dafür gibt es eine Reihe neuer Flags für den Compiler, den Archiver und die JVM. Beipielsweise gibt es den oben schon erwähnten Modulepath und entsprechende Compiler-Optionen  - - module-source-path und  - - module-path .  Diese Optionen sind das Gegenstück zu den alten Optionen  -srcpath und  -classpath .  

 
 

Beim Start der Applikation wird ebenfalls der Modulepath angeben mit der JVM-Option  --module-path (oder kurz  -p) .  Mit der JVM-Option  --module (oder kurz -m ) wird der Modul spezifiziert, der die Klasse mit der  main()- Methode enthält.  Neu ist auch das JVM-Flag  -Xdiag:resolver .  Es ist das Gegenstück zum  -verbose:class Flag und liefert Informationen darüber, wo welche Module gefunden wurden.
 
 

Das  jar -Tool ist erweitert worden, so dass man damit modulare JAR-Dateien erzeugen kann.  Man packt den Modul-Deskriptor, d.h. die Datei  module-info.class , zusammen mit allen zugehörigen Klassen und Ressourcen in eine solche modulare JAR-Datei.   Das heißt, jeder Modul entspricht einer modularen JAR-Datei.  Beim Erzeugen der JAR-Datei kann man mit der Option  --main-class (oder kurz  -e ) die Klasse spezifizieren, die die Klasse mit der  main()- Methode enthält, falls es eine ausführbare, modulare JAR-Datei sein soll.
 
 

Mit dem neuen  jlink -Tool kann man einen Modul und alles, was er braucht (d.h. seine transitiven Abhängigkeiten) zu einem sogenannten Modular Runtime Image zusammenfassen.  Das Resultat dieses Linkings ist keine ausführbare Datei, wie sie z.B. Linker in Sprachen wir C oder C++ erzeugen.  Vielmehr ist ein Modular Runtime Image eine Directory-Struktur ähnlich der des JDK oder JRE.  Darin sind alle Module und Ressourcen abgelegt, von denen der Root-Module abhängt.  Mit diesem Werkzeug kann man Deployments machen, in denen wirklich nur das enthalten ist, was gebraucht wird.  Da ist dann u.U. nicht das gesamte JRE dabei, sondern möglicherweise nur ein Subset der JRE-Module, wenn die Applikation nicht alles aus dem JRE braucht.
 
 

Die Migrationsphase

Mit der Einführung des Modul-Systems ist beabsichtigt, dass langfristig alle Java-Anwendungen modular sein sollen.  Oracle ist voran gegangen und hat den JDK für Java 9 bereits modularisiert.  Populäre Bibliotheken und Frameworks werden wahrscheinlich dem Beispiel von Oracle folgen und irgendwann in modularer Form vorliegen.  Für die eigenen Anwendungen ist man ebenfalls aufgefordert, diese zu modularisieren.  Wenn man überlegt, dass die Modularisierung des JDK etwa fünf Jahre lang gedauert hat, dann ist absehbar, dass es eine längere Transitionsphase geben wird, während der nicht alle Komponenten gleichzeitig in modularer Form vorliegen werden.  Man wird in dieser Migrationsphase mit einem Mix von traditionellen und modularen Komponenten umgehen müssen.  Was ist dafür zu beachten?

 
 

Im Moment ist nur der JDK modularisiert:
 
 

Abb. 5: Nicht-modulare Anwendung mit modularem JDK 9
 
 

Wie oben beschrieben, kann man nun das jdeps -Tool hernehmen, sich die Abhängigkeiten zwischen den Packages der eigenen Applikation ansehen, ein Konzept für die Aufteilung in Module machen, die Modul-Deskriptoren schreiben und am Ende ist die eigene Anwendung modularisiert.  Möglicherweise liegen aber die benutzten Third-Party-Bibliotheken oder Framework noch nicht in modularer Form vor.
 
 

Abb. 6: Modulare Anwendung mit nicht-modularen Komponenten
 
 

Dann hat man das Problem, dass man in den eigenen Modul-Deskriptoren die Abhängigkeiten zu den nicht-modularen Third-Party-Bibliotheken nicht beschreiben kann.  Man kann nur " requires <module> " sagen und wenn die Bibliothek nicht modular ist, dann gibt es keine Module, von denen man lesen könnte.
 
 

Die Lösung für dieses Problem sind die sogenannten Automatic Modules .   Sie entstehen, wenn die JAR-Dateien von nicht-modularen Komponenten auf den Modulepath gelegt werden.   Das Modul-System erzeugt für jede nicht-modulare JAR-Datei auf dem Modulepath einen Modul, dessen Name aus dem Namen der JAR-Datei abgeleitet wird.   Diese automatischen Module haben dann einen Namen, der in den  requires -Anweisungen der eigenen Modul-Deskriptoren verwendet werden kann.  Das ist möglicherweise nicht der Name, den die Third-Party-Bibliothek in ihrer finalen, modularen Form verwenden wird, so dass man die eigenen Modul-Deskriptoren u.U. später noch einmal anpassen muss.  Aber für die Übergangszeit kann man mit den automatischen Modulen arbeiten.
 
 

Automatische Module haben die besondere Eigenschaft, dass sie implizit alle ihre Packages exportieren und alle anderen Module benötigen.  Man kann also in der eigenen modularen Applikation alles aus der Third-Party-Bibliothek benutzen.  Gleichzeitig hat ein automatischer Modul implizite  requires -Beziehungen zu sämtlichen anderen Modulen, inklusive dem unbenannten Modul.
 
 

Abb. 7: Modulare Anwendung mit automatischen und unbenannten Modulen
 
 

Nach der Migrationsphase sollten alle Komponenten modular sein, so dass es den unbenannten und die automatischen Module nicht mehr geben sollte.  Die Verwendung von automatischen und unbenannten Modules ist lediglich für die Übergangsphase gedacht.

Vorbehalte gegen das Modul-System

Versuchen wir abschließend, das Modul-System einzuordnen und zu bewerten.  Es ist lange und heftig diskutiert worden und es gibt eine Reihe von Kritikpunkten.  Schauen wir uns einige davon an.

 
 

Kritikpunkt #1:  public bedeutet nicht mehr  public .
 
 

Bislang war es so, dass man einen Typ, der in einem Package  public deklariert war, in allen anderen Packages benutzen konnte.  Man konnte an der  public -Deklaration sehen, dass etwas allgemein verfügbar ist.  Das ist in Java 9 nicht mehr so.
 
 

Mit dem Modul-System gibt es nun zwei Stellen, an denen Aussagen über die Verfügbarkeit von Typen, Methoden, etc. gemacht werden:  es gibt immer noch die alten Deklarationen als  private protected , package-visible und  public und dazu gibt es die  exports - und  requires -Anweisungen in den Modul-Deskriptoren.  Wenn eine Klasse  public deklariert ist, dann heißt es noch lange nicht, dass sie in einem anderen Modul verwendet werden kann.  Nur wenn der Modul, der die Klasse verwenden will, eine  requires -Anweisung auf den Modul hat, zu dem das Package der Klasse gehört, und es eine umgekehrte  exports -Anweisung gibt, kann die  public -deklarierte Klasse benutzt werden.  Man muss also an mehreren Stellen nachschauen, um heraus zu finden, welche Teile welcher APIs wem zugänglich sind.  Das ist verwirrend und unübersichtlich und trägt nicht unbedingt zur Vereinfachung der Zugriffskontrolle bei.
 
 

Kritikpunkt #2: Erheblicher Aufwand.
 
 

Es gibt Bedenken den Aufwand betreffend, den es kosten wird, größere Anwendungen zu modularisieren.  Die Modularisierung ist an sich keine triviale Aufgabe, weil es eine projektweite Angelegenheit ist, die die Struktur der gesamten Anwendung betrifft.  Wie aufwändig es sein kann, hat man an der Modularisierung des JDK gesehen: es hat fünf Jahre gedauert.   Wie viel Aufwand es für die eigene Anwendung sein wird, hängt davon ab, ob JDK-interne APIs aus den  sun.* -packages verwendet werden, ob Split Package vorkommen, ob direkt auf das Deployment des JDK (z.B. auf die Datei  rt.jar ) zugegriffen wird, etc.   Eine gut strukturierte Anwendung, die keine JDK-Interna benutzt, lässt sich leichter modularisieren als eine, die jeden denkbaren Trick und Kniff ausnutzt.
 
 

Kritikpunkt #3: Schwer lesbare Modul-Deskriptoren.
 
 

Für die  exports -Direktiven gibt es keine Package-Wildcards, d.h. man muss alle Packages, die exportiert werden sollen, einzeln in der  module-info.java -Datei auflisten.  Das könnte zu umfangreichen, schwer lesbaren Modul-Deskriptoren führen. Man kann sich zwar durch das  jdeps -Werkzeug mit der Option  --generate-module-info aus einer nicht-modularen JAR-Datei eine erste Vorlage für den Modul-Deskriptor generieren lassen, in dem alle Packages explizit exportiert werden.  Damit spart man am Anfang eine Menge Tipparbeit.  Aber bei jedem späteren Refactoring muss u. U. auch der Modul-Deskriptor angepasst werden, was je nach Komplexität der Module-Deskriptoren mühselig und fehleranfällig sein könnte.  Wie weit IDEs und Build-Tools dabei helfen werden, bleibt anzuwarten.
 
 

Kritikpunkt #4: Keine Modul-Versionen.
 
 

Es ist im Java-Modul-System nicht vorgesehen, dass die Module Versionen haben.  Insbesondere ist es nicht ohne weiteres möglich, mehrere Versionen eines Moduls gleichzeitig zu verwenden.  Mit anderen Worten, eine einfache Lösung für das "jar hell"-Problem bietet das Modul-System nicht.  Es gibt zwar sogenannte Laufzeit-Layer, mit denen man eine Lösung bauen kann, aber das geht weit über die "normale" Benutzung des Modul-Systems hinaus.  Solche Lösungen sollen auch nicht die Java-Entwickler bauen, sondern es wird von Oracle als Aufgabe für die Build-Tools angesehen.  Der Grund für das Fehlen von Modul-Versionen liegt darin, dass schon relativ früh in den Diskussion um das Modul-Konzept erkennbar war, dass man sich weder auf die Syntax noch auf die Semantik eines Versions-Schemas würde einigen können.  Deshalb ist die Versionierung ausdrücklich als Non-Requirement in der Spezifikation genannt.
 
 

Damit belassen wir es bei diesem kurzen Einblick in das Java Modul-System.  Es hat zahlreiche weitere Features, die ganze Bücher füllen werden.  Es sind bereits drei Buchveröffentlichung zum Modul-System in Arbeit (siehe / OREIL /, / APRES / und / MANNG /).
 
 

 
 

If you are interested to hear more about this and related topics you might want to check out the following seminars:
Seminars
Java Module System - Names, Unnamed, Automatic Modules, Module Descriptors, Tool Chain
1 day seminar ( open enrollment and on-site)
Java 8 & 9 - Lambdas & Stream, New Concurrency Utilities, Date/Time API, Module System
4 day seminar ( open enrollment and on-site)
 

 
 
 

  © Copyright 1995-2018 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/91.Java9.What-is-new-in-Java-9/90.java-9.1.overview.ready_6.html  last update: 26 Oct 2018