Jigsaw

+ andere TechDocs
+ OSGi
+ Docker
+


Mit Java 9 wird das Modularisierungssystem Jigsaw eingeführt (wörtlich übersetzt: Stichsäge oder Puzzle). Die Modularisierung bezieht sich auf zwei verschiedene Bereiche:

Wichtige Ziele von Jigsaw sind:

Mit Jigsaw definierte Module sind voneinander abgekapselt. Abhängigkeiten müssen explizit deklariert werden (mit requires und exports). Das hat Auswirkungen auf viele Bereiche wie beispielsweise: Entwicklung, Kompilierung, Testen, Packaging, Deployment und Laufzeitumgebung. Außer direkten Abhängigkeiten können auch lose gekoppelte "Service Consumer" (mit uses) und "Service Provider" (mit provides) definiert werden.

Anders als OSGi bietet Jigsaw keine Services Registry, kein Life-Cycle-Modell, keine dynamische Versionierung und kein "Hot Deployment". Dafür ist Jigsaw einfacher und bietet Modularisierung in allen Phasen, insbesondere bereits beim Kompilieren.



Inhalt

  1. Vergleich mit anderen Modularisierungskonzepten
    Java Access Modifier / Visibility Modifier, Jar-Libs, Jigsaw, OSGi, Docker
  2. Wichtige Änderungen in Java 9 und durch Jigsaw
    Änderungen und Inkompatibilitäten, Infos zu Java 9 und Jigsaw
  3. Modulepath, Jigsaw-Modularten sowie Konventionen zur Benamung und zur Verzeichnisstruktur
    Modulepath versus Classpath, Arten von Jigsaw-Modulen, Modul-Name, Verzeichnisstruktur
  4. Jigsaw-Demo mit Modulabhängigkeiten
    Verzeichnisstruktur und Java-Dateien, module-info.java, MeineApp.java, MeinUtil.java, Installation des JDK 9, Aktivieren von Java 9, Kompilieren mit "module-source-path" und direktes Ausführen, Depenency: Package versus Module, Modular Jars erstellen und ausführen, Benamung, Modular-Jar-Analyse, Kompilieren ohne "module-source-path", Mit jlink eine ausführbare Distribution inklusive abgespektem JDK erstellen, Bemerkenswertes
  5. Jigsaw-Demo mit Service-Provider und -Consumer
    Verzeichnisstruktur und Java-Dateien, module-info.java, MeinServiceInterface.java, MeineServiceImplementierungA.java, MeineServiceImplementierungB.java, MeinServiceConsumer.java, Modular Jars erstellen und ausführen, Bemerkenswertes
  6. Jigsaw-Demo mit einem "Automatic Module" und mit "Implied Readability"
    Verzeichnisstruktur und Java-Dateien, module-info.java, MeineApp.java, MeinUtil.java, Kompilieren, Bauen und Ausführen, Bemerkenswertes
  7. Jigsaw-Demo mit doppelten Klassen und verschiedenen Modularten
    Anlage der Verzeichnisstruktur und Erzeugen der Java-Dateien, Jigsaw-Moduldeskriptordateien, Java-Klassen, Kompilieren und Bauen, Ausführung auf verschiedene Arten
  8. Abhängigkeitsgraph mit Graphviz



Vergleich mit anderen Modularisierungskonzepten

Java Access Modifier / Visibility Modifier (Zugriffs-/Sichtbarkeitsmodifizierer)
Über die Access Modifier private, protected, public, sowie default (ohne Modifier), kann die Sichtbarkeit von Feldern, Methoden und Klassen definiert werden. Damit ist allerdings keine wirkliche Modularisierung und gezielte Abhängigkeitskontrolle möglich, da die Attribute anwendungsweit gelten. public bedeutet global für alle öffentlich. Und die anderen einschränkenden Modifier erlauben keine gezielte Veröffentlichung für bestimmte andere Packages.
Jar-Libs
Die Bündelung von Klassen in Jar-Libs erhöht die Übersichtlichkeit, verdeutlicht die gewollte Struktur und erleichtert die Weitergabe von Bibliotheken. Aber die Aufteilung in Jars bedeutet keine wirkliche Modularisierung, weil auch hierbei keine Abhängigkeitskontrolle möglich ist. Die Namen der Jar-Libs und die Grenzen zwischen den Jar-Libs haben für die JVM und den ClassLoader keine Bedeutung: Alle Klassen aller Jar-Libs befinden sich gleichermaßen im gemeinsamen Namespace im CLASSPATH und es gilt weiterhin: public ist global für alle öffentlich.
Von der "JAR Hell" spricht man, wenn mehrere zu einer Anwendung zusammengefügte Jar-Libs die gleichen Klassen in unterschiedlichen Versionen enthalten. Dann kann es von der zufälligen Reihenfolge im CLASSPATH abhängen, welche Version geladen wird.
Jigsaw
Mit Java 9 wird das Modularisierungssystem Jigsaw eingeführt. Damit wird erstmalig ein Modulsystem Bestandteil von Java, welches explizite Abhängigkeitskontrolle verwendet. Jigsaw-Module ("Modular Jars") werden von der JVM und vom ClassLoader erkannt. Sie müssen alle benötigten Fremd-Module explizit deklarieren (mit requires) und eigene öffentlich zur Verfügung gestellte Packages explizit benennen (mit exports). Lose gekoppelte "Services" werden unterstützt, indem "Service Consumer" (mit uses) und "Service Provider" (mit provides) definiert werden. Jigsaw ermöglicht echte Kapselung und Modularisierung, und zwar in allen Phasen, insbesondere bereits beim Kompilieren. Anders als bei OSGi werden bei Jigsaw zur Trennung der Module nicht getrennte ClassLoader verwendet.
OSGi
OSGi gibt es bereits seit dem Jahr 2000. Es bietet ein sehr ausgereiftes gut funktionierendes Modularisierungssystem mit expliziter Abhängigkeitskontrolle, welches in vielen Produkten Verwendung findet. OSGi ist etwas aufwändiger als Jigsaw, aber anders als Jigsaw bietet OSGi viele über die reine Modularisierung hinausgehende Optionen, beispielsweise:
- Versionierung wird unterstützt. Zu einer Komponente können gleichzeitig mehrere Versionen in Betrieb sein.
- Life-Cycle-Management. Komponenten können während des laufenden Betriebs hinzugefügt und ausgetauscht werden ("Hot Deployment", "Hot Plugging"), auch übers Internet.
- Eine Service Registry bietet die Suche nach passenden Komponenten zur Laufzeit.
- Service-Orientierung und lose Kopplung werden optimal unterstützt.
Docker
Docker bietet eine viel weiter gehende Kapselung und Modularisierung. Die Module laufen in getrennten Containern und damit auch in getrennten Java-VMs, oder sogar in einer gänzlich anderen Programmiersprache. Eine Kommunikation zwischen den Modulen in getrennten Docker-Containern erfolgt über Netzwerkschnittstellen (z.B. TCP/IP, REST etc.) und explizit freigegebenen Portnummern.
In Docker-Containern realisierte Dienste sind häufig einzeln deploybar sowie durch mehrfache Instanziierung leicht skalierbar.
Während mit Jigsaw und OSGi realisierte Dienste manchmal als Nanoservice bezeichnet werden, fallen in Docker-Containern realisierte Dienste eher in die Rubrik Microservices und Self-contained Systems (SCS).


Wichtige Änderungen in Java 9 und durch Jigsaw

Durch Java 9 und Jigsaw ergeben sich Änderungen in vielen Bereichen, wie beispielsweise Entwicklung, Kompilierung, Testen, Packaging, Deployment und Laufzeitumgebung. Einige Tools werden ohne Anpassung nicht lauffähig sein, beispielsweise aus folgenden Gründen:

Infos zu Java 9 und Jigsaw finden Sie unter:



Modulepath, Jigsaw-Modularten sowie Konventionen zur Benamung und zur Verzeichnisstruktur

Modulepath versus Classpath

Bis Java 8 gab es nur den Classpath und keinen Modulepath. Alle Klassen und alle Jar-Libs befinden sich gleichermaßen im gemeinsamen Namespace im CLASSPATH und public ist global für alle öffentlich.

Ab Java 9 haben Sie die Wahl: Sie können weiterhin nur den konventionellen Classpath verwenden (mit -cp), Sie können Jigsaw-Module im Modulepath verwenden (mit -p), und Sie können beides kombinieren. Die Neuerungen und Vorteile durch Jigsaw gelten natürlich nur für Jigsaw-Module im Modulepath. Die Summe der Module von der Java-Runtime-Umgebung und der Module im Modulepath werden "Observable Modules" genannt.

Außer dem Modulepath (per --module-path oder -p) gibt es für den javac-Compiler noch den Modulesourcepath (per --module-source-path), über den angegebenen werden kann, wo sich Sourcen zu anderen Modulen befinden (siehe das Beispiel weiter unten).

Arten von Jigsaw-Modulen

Es gibt fünf verschiedene Arten von Modulen (außer dem "Unnamed Module" sind alle anderen "Named Modules"):

Konventionen zur Benamung

Der Name eines Jigsaw-Moduls kann frei gewählt werden, aber er muss eindeutig sein. Empfohlen wird dasselbe "reverse-domain-name Pattern" wie auch bei Java-Packages, also beginnend mit der umgekehrten Webdomainadresse, alles in Kleinschreibung und separiert durch Punkte, beispielsweise so: de.meinefirma.meinerstesmodul.

Das führt dazu, dass dieser Name üblicherweise an mehreren Stellen mit unterschiedlicher Bedeutung verwendet wird:

Dies wird weiter unten noch mal aufgegriffen.

Verzeichnisstruktur

Als Sourcecode-Verzeichnisstruktur für ein Projekt bestehend aus mehreren Jigsaw-Modulen in einem gemeinsamen src-Verzeichnis wird eine Struktur ähnlich zu dieser empfohlen:

[\MeinWorkspace\MeinProjektverzeichnis]
 '- [src]
     |- [de.meinefirma.meinerstesmodul]
     |   |- [de]
     |   |   '- [meinefirma]
     |   |       '- [meinerstesmodul]
     |   |           '- MeineApp.java
     |   '- module-info.java
     '- [de.meinefirma.meinzweitesmodul]
         |- [de]
         |   '- [meinefirma]
         |       '- [meinzweitesmodul]
         |           '- [meinunterpackage]
         |               '- MeineKlasseX.java
         '- module-info.java

Das Besondere dabei ist, dass pro Modul der Sourcecode in einem Unterverzeichnis mit dem Namen des Moduls angeordnet werden soll.



Jigsaw-Demo mit Modulabhängigkeiten

JigsawDepDemo-Graphviz Diese Demo zeigt:

Sie können die folgenden Programmierbeispiele entweder als Zipdatei downloaden oder Schritt für Schritt aufbauen, wie im Folgenden beschrieben wird.

Die Kommandos sind für Windows dargestellt. Falls Sie Linux oder Mac OS X verwenden, genügt es in der Regel, wenn Sie in Pfadangaben "\" durch "/", in PATH-Angaben ";" durch ":" und bei Platzhaltern %MEINE_VARIABLE% durch $MEINE_VARIABLE ersetzen.

Anlage der Verzeichnisstruktur und Erzeugen der Java-Dateien

  1. Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:

    cd \MeinWorkspace

    mkdir JigsawDepDemo

    cd JigsawDepDemo

    mkdir src\de.meinefirma.meineapp\de\meinefirma\meineapp

    mkdir src\de.meinefirma.meinutil\de\meinefirma\meinutil

    tree /F

  2. Erstellen Sie im src\de.meinefirma.meineapp-Verzeichnis die Jigsaw-Moduldeskriptordatei für das Hauptmodul:
    module-info.java

    module de.meinefirma.meineapp
    {
       // Importiertes Modul:
       requires de.meinefirma.meinutil;
    }
    
  3. Erstellen Sie im src\de.meinefirma.meinutil-Verzeichnis die Jigsaw-Moduldeskriptordatei für das Hilfsmodul:
    module-info.java

    module de.meinefirma.meinutil
    {
       // Exportiertes Package:
       exports de.meinefirma.meinutil;
    }
    
  4. Erstellen Sie im src\de.meinefirma.meineapp\de\meinefirma\meineapp-Verzeichnis die Hauptapplikationsklasse (im "Initial Module"):
    MeineApp.java

    package de.meinefirma.meineapp;
    
    import java.lang.reflect.Layer;
    import de.meinefirma.meinutil.MeinUtil;
    
    public class MeineApp
    {
       public static void main( String[] args )
       {
          System.out.println( "CLASSPATH:           " + System.getProperty( "java.class.path" ) );
          System.out.println( "Class / Modul:       " + MeineApp.class.getSimpleName() + " aus " + MeineApp.class.getModule() +
                                                 ", " + MeinUtil.class.getSimpleName() + " aus " + MeinUtil.class.getModule() );
          Layer lr = MeinUtil.class.getModule().getLayer();
          if( lr != null ) {
             System.out.println( "Layer.Configuration: " + lr.configuration() );
             System.out.println( "Layer.Modules:       " + lr.modules() );
          }
          System.out.println( "ProcessHandle-Infos: " + MeinUtil.getProcessInfos() );
       }
    }
    
  5. Erstellen Sie im src\de.meinefirma.meinutil\de\meinefirma\meinutil-Verzeichnis die Hilfsklasse:
    MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static String getProcessInfos()
       {
          return "pid: "    + ProcessHandle.current().getPid() +
                 ", user: " + ProcessHandle.current().info().user() +
                 ", cmd: "  + ProcessHandle.current().info().command();
       }
    }
    
  6. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es unter Windows mit tree /F und unter Linux mit tree):

    [\MeinWorkspace\JigsawDepDemo]
     '- [src]
         |- [de.meinefirma.meineapp]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meineapp]
         |   |           '- MeineApp.java
         |   '- module-info.java
         '- [de.meinefirma.meinutil]
             |- [de]
             |   '- [meinefirma]
             |       '- [meinutil]
             |           '- MeinUtil.java
             '- module-info.java
    

Installation des JDK 9

  1. Installation des JDK 9:

  2. Überprüfen Sie anschließend, ob bei JAVA_HOME und im PATH die gewünschte defaultmäßig zu nutzende Java-Version eingetragen ist (dies braucht nicht JDK 9 zu sein):

    echo JAVA_HOME=%JAVA_HOME%  bzw.  echo JAVA_HOME=$JAVA_HOME

    javac -version

    java -version

Aktivieren von Java 9

  1. Falls Sie defaultmäßig eine andere Java-Version nutzen (z.B. Java 8), dann müssen Sie für alle folgenden Schritte für das aktuelle Kommandozeilenfenster auf Java 9 umschalten (passen Sie den Pfad zu Ihrer Java-9-Installation an):

    Die letzten drei Kommandos müssen Java 9 melden.

Kompilieren mit "module-source-path" und direktes Ausführen des Beispiels

  1. Kompilieren Sie alle vier Java-Klassen:

    Diese vereinfachte Vorgehensweise mit --module-source-path funktioniert so nur, wenn beim Kompilieren von de.meinefirma.meineapp der Sourcecode von de.meinefirma.meinutil zur Verfügung steht. Weiter unten ist beschrieben, wie es ohne --module-source-path geht (nur dann könnten die Modulsourcen auch auf verschiedenen PCs sein).

  2. Führen Sie die Anwendung aus:

    java -p classes -m de.meinefirma.meineapp/de.meinefirma.meineapp.MeineApp

    Sie erhalten unter anderem (die weiteren Ausgaben sollen vorerst noch nicht interessieren):

    ...
    Class / Modul:       MeineApp aus module de.meinefirma.meineapp, MeinUtil aus module de.meinefirma.meinutil
    ...
    

    Das JDK 9 hat die Modul-Struktur erkannt, und die Anwendungsklasse MeineApp im Modul de.meinefirma.meineapp kann auf die Hilfsmethode MeinUtil.getProcessInfos() aus dem Modul de.meinefirma.meinutil zugreifen.

  3. Löschen Sie testweise
    in src\de.meinefirma.meineapp\module-info.java die Zeile requires de.meinefirma.meinutil; oder
    in src\de.meinefirma.meinutil\module-info.java die Zeile exports de.meinefirma.meinutil;.
    Sie erhalten die Fehlermeldung:
    error: package de.meinefirma.meinutil does not exist

  4. Fügen Sie für die folgenden Versuche die beiden Zeilen wieder ein und kompilieren Sie erneut.

  5. Beachten Sie:

    Häufig sind die Package-Namen und Modul-Namen identisch, so auch in diesem Beispiel: beide lauten: de.meinefirma.meinutil.

Pro Modul ein Modular Jar erstellen und die Anwendung damit ausführen

  1. Normale Jar-Libs sind per Zip zusammengefügte Archive von .class-Dateien plus weiteren Ressourcendateien. Mit Jigsaw gibt es eine Neuerung: Wenn eine solche Jar-Lib im Root-Verzeichnis einen "Jigsaw Module Descriptor" in Form einer module-info.class-Datei enthält, ist dies eine so genannte "Modular Jar".

  2. Erzeugen Sie pro Modul eine "Modular Jar" und führen Sie wieder die Hauptklasse MeineApp aus, aber diesmal mit dem "Initial Modular Jar" (vergessen Sie nicht den Punkt am Ende der beiden jar-Kommandos):

    mkdir modules

    jar --create --file modules/de.meinefirma.meineapp-1.0.jar --module-version 1.0 --main-class de.meinefirma.meineapp.MeineApp -C classes/de.meinefirma.meineapp .

    jar --create --file modules/de.meinefirma.meinutil-1.0.jar --module-version 1.0 -C classes/de.meinefirma.meinutil .

    java -p modules -m de.meinefirma.meineapp/de.meinefirma.meineapp.MeineApp

    Beachten Sie, dass der Modulpfad hinter -p anders als beim vorherigen Beispiel diesmal nicht classes lautet, sondern modules.

  3. Da wir beim Bauen der "Initial Modular Jar" mit dem Parameter --main-class die Hauptanwendungsklasse MeineApp angegeben haben, können wir die Anwendung auch mit einem kürzeren Kommando starten, bei dem nur der Modulname ausreicht:

    java -p modules -m de.meinefirma.meineapp

  4. Beachten Sie die vier verschiedenen Bedeutungen von "de.meinefirma.meineapp":

    Beachten Sie, dass diese Regeln nicht alle zwingend sind, aber üblichen Konventionen entsprechen.

Analyse der Modular Jars mit print-module-descriptor und mit jdeps

  1. Lassen Sie sich die Moduldeskriptoren mit jar anzeigen:

    jar --print-module-descriptor --file=modules/de.meinefirma.meineapp-1.0.jar

    jar --print-module-descriptor --file=modules/de.meinefirma.meinutil-1.0.jar

  2. Analysieren Sie die Dependencies mit jdeps:

    jdeps modules/*.jar

  3. Führen Sie weitere Analysen mit javap durch:

    javap -verbose classes\de.meinefirma.meineapp\module-info.class

    javap -verbose classes\de.meinefirma.meinutil\module-info.class

Kompilieren ohne "module-source-path" (z.B. falls getrennter Soucecode)

Angenommen, die Sourcen zu den Modulen liegen auf verschiedenen PCs (z.B. weil verschiedene Projektgruppen daran arbeiten): Dann funktioniert die oben beschriebene Vorgehensweise mit --module-source-path nicht. In diesem Fall werden die Modul-Jars weitergereicht (üblicherweise über ein zentrales Repository). Führen Sie hierzu folgende Schritte aus (die Kommandos sind so gestaltet, dass sie testweise auch auf einem einzigen PC ausgeführt werden können):

  1. Erster PC:

    cd \MeinWorkspace\JigsawDepDemo

    mkdir modules

    javac -d classes\de.meinefirma.meinutil src\de.meinefirma.meinutil\*.java src\de.meinefirma.meinutil\de\meinefirma\meinutil\*.java

    jar --create --file modules/de.meinefirma.meinutil-1.0.jar --module-version 1.0 -C classes/de.meinefirma.meinutil .

  2. Transfer der resultierenden de.meinefirma.meinutil-1.0.jar zum zweiten PC in das modules-Verzeichnis und dann auf dem zweiten PC:

    javac -p modules -d classes\de.meinefirma.meineapp src\de.meinefirma.meineapp\*.java src\de.meinefirma.meineapp\de\meinefirma\meineapp\*.java

    jar --create --file modules/de.meinefirma.meineapp-1.0.jar --module-version 1.0 --main-class de.meinefirma.meineapp.MeineApp -C classes/de.meinefirma.meineapp .

  3. Ausführung der Anwendung:

    java -p modules -m de.meinefirma.meineapp

Mit jlink eine ausführbare Distribution inklusive abgespektem JDK erstellen

  1. Seit Java 9 gibt es mit jlink einen Linker, der alle benötigten Module sowohl aus der Anwendung als auch vom JDK ermittelt und damit eine Distribution erstellt, die ohne zusätzliches Java ausführbar ist, da sie ein eigenes abgespecktes Java enthält ("Custom Modular Run-time image"):

    Führen Sie das Ergebnis aus:

  2. Sie erhalten:

    CLASSPATH:
    Class / Modul:       MeineApp aus module de.meinefirma.meineapp, MeinUtil aus module de.meinefirma.meinutil
    Layer.Configuration: de.meinefirma.meineapp, de.meinefirma.meinutil, java.base
    Layer.Modules:       [module java.base, module de.meinefirma.meinutil, module de.meinefirma.meineapp]
    ProcessHandle-Infos: pid: ..., user: Optional[...\...], cmd: Optional[\MeinWorkspace\JigsawDepDemo\distribution\bin\java.exe]
    
  3. Beachten Sie:

Resultierende Projektstruktur

  1. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):

    [\MeinWorkspace\JigsawDepDemo]
     |- [classes]
     |   |- [de.meinefirma.meineapp]
     |   |   '- ...
     |   '- [de.meinefirma.meinutil]
     |       '- ...
     |- [distribution]
     |   |- [bin]
     |   |   '- ...
     |   |- [conf]
     |   |   '- ...
     |   |- [lib]
     |   |   '- ...
     |   '- release
     |- [modules]
     |   |- de.meinefirma.meineapp-1.0.jar
     |   '- de.meinefirma.meinutil-1.0.jar
     '- [src]
         |- [de.meinefirma.meineapp]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meineapp]
         |   |           '- MeineApp.java
         |   '- module-info.java
         '- [de.meinefirma.meinutil]
             |- [de]
             |   '- [meinefirma]
             |       '- [meinutil]
             |           '- MeinUtil.java
             '- module-info.java
    


Jigsaw-Demo mit Service-Provider und -Consumer

Diese Demo zeigt:

Anlage der Verzeichnisstruktur und Erzeugen der Java-Dateien

  1. Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:

    cd \MeinWorkspace

    mkdir JigsawServiceDemo

    cd JigsawServiceDemo

    mkdir src\de.meinefirma.meininterface\de\meinefirma\meininterface

    mkdir src\de.meinefirma.meinprovider\de\meinefirma\meinprovider

    mkdir src\de.meinefirma.meinconsumer\de\meinefirma\meinconsumer

    tree /F

  2. Erstellen Sie im src\de.meinefirma.meininterface-Verzeichnis den Jigsaw-Module-Descriptor für das Service-Interface: module-info.java

    module de.meinefirma.meininterface {
       exports de.meinefirma.meininterface;
    }
    
  3. Erstellen Sie im src\de.meinefirma.meinprovider-Verzeichnis den Jigsaw-Module-Descriptor für den Service-Provider: module-info.java

    module de.meinefirma.meinprovider {
       requires transitive de.meinefirma.meininterface;
       provides de.meinefirma.meininterface.MeinServiceInterface with
                de.meinefirma.meinprovider.MeineServiceImplementierungA,
                de.meinefirma.meinprovider.MeineServiceImplementierungB;
    }
    
  4. Erstellen Sie im src\de.meinefirma.meinconsumer-Verzeichnis den Jigsaw-Module-Descriptor für den Service-Konsumenten: module-info.java

    module de.meinefirma.meinconsumer {
       requires de.meinefirma.meininterface;
       uses     de.meinefirma.meininterface.MeinServiceInterface;
    }
    
  5. Erstellen Sie im src\de.meinefirma.meininterface\de\meinefirma\meininterface-Verzeichnis die Service-Interface-Klasse: MeinServiceInterface.java

    package de.meinefirma.meininterface;
    
    public interface MeinServiceInterface
    {
       String getInfos();
    }
    
  6. Erstellen Sie im src\de.meinefirma.meinprovider\de\meinefirma\meinprovider-Verzeichnis die erste Service-Provider-Klasse: MeineServiceImplementierungA.java

    package de.meinefirma.meinprovider;
    
    import de.meinefirma.meininterface.MeinServiceInterface;
    
    public class MeineServiceImplementierungA implements MeinServiceInterface
    {
       @Override
       public String getInfos()
       {
          return "  A: ProcessHandle-Infos: pid: " + ProcessHandle.current().getPid() +
                                        ", user: " + ProcessHandle.current().info().user() +
                                        ", cmd: "  + ProcessHandle.current().info().command();
       }
    }
    
  7. Erstellen Sie im src\de.meinefirma.meinprovider\de\meinefirma\meinprovider-Verzeichnis die zweite Service-Provider-Klasse: MeineServiceImplementierungB.java

    package de.meinefirma.meinprovider;
    
    import de.meinefirma.meininterface.MeinServiceInterface;
    
    public class MeineServiceImplementierungB implements MeinServiceInterface
    {
       @Override
       public String getInfos()
       {
          return "  B: Service-Provider:    " + MeineServiceImplementierungB.class.getSimpleName() +
                                      " aus " + MeineServiceImplementierungB.class.getModule();
       }
    }
    
  8. Erstellen Sie im src\de.meinefirma.meinconsumer\de\meinefirma\meinconsumer-Verzeichnis die Service-Konsument-Klasse: MeinServiceConsumer.java

    package de.meinefirma.meinconsumer;
    
    import java.util.Iterator;
    import java.util.ServiceLoader;
    import de.meinefirma.meininterface.MeinServiceInterface;
    
    public class MeinServiceConsumer
    {
       public static void main( String[] args )
       {
          System.out.println( "" );
          Iterator<MeinServiceInterface> iter = ServiceLoader.load( MeinServiceInterface.class ).iterator();
          if( !iter.hasNext() ) {
             System.out.println( "Keine Implementierung zu 'MeinServiceInterface' vorhanden." );
          }
          while( iter.hasNext() ) {
             System.out.println( iter.next().getInfos() );
          }
       }
    }
    
  9. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):

    [\MeinWorkspace\JigsawServiceDemo]
     '- [src]
         |- [de.meinefirma.meinconsumer]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meinconsumer]
         |   |           '- MeinServiceConsumer.java
         |   '- module-info.java
         |- [de.meinefirma.meininterface]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meininterface]
         |   |           '- MeinServiceInterface.java
         |   '- module-info.java
         '- [de.meinefirma.meinprovider]
             |- [de]
             |   '- [meinefirma]
             |       '- [meinprovider]
             |           |- MeineServiceImplementierungA.java
             |           '- MeineServiceImplementierungB.java
             '- module-info.java
    

Aktivieren von Java 9

  1. JDK 9 muss installiert und aktiviert sein:

    Falls Sie defaultmäßig eine andere Java-Version nutzen (z.B. Java 8), dann müssen Sie für alle folgenden Schritte für das aktuelle Kommandozeilenfenster auf Java 9 umschalten (passen Sie den Pfad zu Ihrer Java-9-Installation an) (falls Sie Linux verwenden: siehe oben):

    set JAVA_HOME=C:\Program Files\Java\jdk-9

    PATH=%JAVA_HOME%\bin;%PATH%

  2. Überprüfen Sie, ob bei JAVA_HOME und im PATH das JDK 9 korrekt eingetragen ist:

    echo JAVA_HOME=%JAVA_HOME%

    javac -version

    java -version

    In allen drei Fällen muss Java 9 gemeldet werden.

Kompilieren, bauen der "Modular Jars" und Ausführen der Demo

  1. Kompilieren und Bauen des Interface-Moduls:

    cd \MeinWorkspace\JigsawServiceDemo

    mkdir modules

    javac -d classes\de.meinefirma.meininterface src\de.meinefirma.meininterface\*.java src\de.meinefirma.meininterface\de\meinefirma\meininterface\*.java

    jar --create --file=modules/de.meinefirma.meininterface.jar -C classes/de.meinefirma.meininterface .

  2. Kompilieren und Bauen des Provider-Moduls:

    javac -p modules -d classes\de.meinefirma.meinprovider src\de.meinefirma.meinprovider\*.java src\de.meinefirma.meinprovider\de\meinefirma\meinprovider\*.java

    jar --create --file=modules/de.meinefirma.meinprovider.jar -C classes/de.meinefirma.meinprovider .

  3. Kompilieren und Bauen des Consumer-Moduls:

    javac -p modules -d classes\de.meinefirma.meinconsumer src\de.meinefirma.meinconsumer\*.java src\de.meinefirma.meinconsumer\de\meinefirma\meinconsumer\*.java

    jar --create --file=modules/de.meinefirma.meinconsumer.jar --main-class=de.meinefirma.meinconsumer.MeinServiceConsumer -C classes/de.meinefirma.meinconsumer .

  4. Ausführen der Demo:

    java -p modules -m de.meinefirma.meinconsumer

  5. Sie erhalten zwei Ausgabezeilen, für jeden der beiden Service-Provider eine:

      B: Service-Provider:    MeineServiceImplementierungB aus module de.meinefirma.meinprovider
      A: ProcessHandle-Infos: pid: 3192, user: Optional[TO-I7-950\User], cmd: Optional[C:\Program Files\Java\jdk-9\bin\java.exe]
    

Analyse der Modular Jars mit print-module-descriptor und mit jdeps

  1. Lassen Sie sich die Moduldeskriptoren anzeigen:

    jar --print-module-descriptor --file=modules/de.meinefirma.meininterface.jar

    jar --print-module-descriptor --file=modules/de.meinefirma.meinprovider.jar

    jar --print-module-descriptor --file=modules/de.meinefirma.meinconsumer.jar

  2. Analysieren Sie die Dependencies mit jdeps:

    jdeps -s modules/*.jar

    jdeps modules/*.jar

  3. Beachten Sie:



Jigsaw-Demo mit einem "Automatic Module" und mit "Implied Readability"

Diese Demo zeigt:

Anlage der Verzeichnisstruktur und Erzeugen der Java-Dateien

  1. Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:

    cd \MeinWorkspace

    mkdir JigsawAutoModImpliedReadDemo

    cd JigsawAutoModImpliedReadDemo

    mkdir modules

    mkdir src\de.meinefirma.meineapp\de\meinefirma\meineapp

    mkdir src\de.meinefirma.meinutil\de\meinefirma\meinutil

    tree /F

  2. Als "Nicht-Jigsaw-Lib" soll die Google-Guava-Lib verwendet werden. Downloaden Sie guava-19.0.jar von http://central.maven.org/maven2/com/google/guava/guava/19.0/ in das modules-Verzeichnis.

  3. Erstellen Sie im src\de.meinefirma.meineapp-Verzeichnis die Jigsaw-Moduldeskriptordatei für das Hauptmodul:
    module-info.java

    module de.meinefirma.meineapp
    {
       requires de.meinefirma.meinutil;
    }
    
  4. Erstellen Sie im src\de.meinefirma.meinutil-Verzeichnis die Jigsaw-Moduldeskriptordatei für das Hilfsmodul:
    module-info.java

    module de.meinefirma.meinutil
    {
       requires transitive guava;
       exports de.meinefirma.meinutil;
    }
    
  5. Erstellen Sie im src\de.meinefirma.meineapp\de\meinefirma\meineapp-Verzeichnis die Hauptapplikationsklasse (im "Initial Module"):
    MeineApp.java

    package de.meinefirma.meineapp;
    
    import com.google.common.base.Joiner;
    import de.meinefirma.meinutil.MeinUtil;
    
    public class MeineApp
    {
       public static void main( String[] args )
       {
          Joiner joiner = Joiner.on( ", " ).skipNulls();
          System.out.println( "\n" + joiner.join( classAndModule( Joiner.class ), classAndModule( MeinUtil.class ), classAndModule( MeineApp.class ) ) );
       }
    
       private static String classAndModule( Class<?> clss )
       {
          return clss.getSimpleName() + " aus " + clss.getModule();
       }
    }
    
  6. Erstellen Sie im src\de.meinefirma.meinutil\de\meinefirma\meinutil-Verzeichnis die Hilfsklasse:
    MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static String dummy()
       {
          return "... wird nicht benutzt";
       }
    }
    
  7. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):

    [\MeinWorkspace\JigsawAutoModImpliedReadDemo]
     '- [modules]
     |   '- guava-19.0.jar
     '- [src]
         |- [de.meinefirma.meineapp]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meineapp]
         |   |           '- MeineApp.java
         |   '- module-info.java
         '- [de.meinefirma.meinutil]
             |- [de]
             |   '- [meinefirma]
             |       '- [meinutil]
             |           '- MeinUtil.java
             '- module-info.java
    

Aktivieren von Java 9

  1. Falls Sie defaultmäßig eine andere Java-Version nutzen (z.B. Java 8), dann müssen Sie für alle folgenden Schritte für das aktuelle Kommandozeilenfenster auf Java 9 umschalten (passen Sie den Pfad zu Ihrer Java-9-Installation an) (falls Sie Linux verwenden: siehe oben):

    set JAVA_HOME=C:\Program Files\Java\jdk-9

    PATH=%JAVA_HOME%\bin;%PATH%

  2. Kontrolle:

    echo JAVA_HOME=%JAVA_HOME%

    javac -version

    java -version

    In allen drei Fällen muss Java 9 gemeldet werden.

Kompilieren, Bauen und Ausführen

  1. Löschen Sie Ergebnisse vorheriger Versuche:

    rd /S /Q classes

    del modules\de.meinefirma.*

  2. Kompilieren und Bauen Sie zuerst das Modul de.meinefirma.meinutil:

    javac -p modules -d classes\de.meinefirma.meinutil src\de.meinefirma.meinutil\*.java src\de.meinefirma.meinutil\de\meinefirma\meinutil\*.java

    jar --create --file modules/de.meinefirma.meinutil-1.0.jar --module-version 1.0 -C classes/de.meinefirma.meinutil .

  3. Kompilieren und Bauen Sie das davon abhängige Modul de.meinefirma.meineapp:

    javac -p modules -d classes\de.meinefirma.meineapp src\de.meinefirma.meineapp\*.java src\de.meinefirma.meineapp\de\meinefirma\meineapp\*.java

    jar --create --file modules/de.meinefirma.meineapp-1.0.jar --module-version 1.0 --main-class de.meinefirma.meineapp.MeineApp -C classes/de.meinefirma.meineapp .

  4. Führen Sie die Anwendung aus:

    java -p modules -m de.meinefirma.meineapp

    Sie erhalten:

    Joiner aus module guava, MeinUtil aus module de.meinefirma.meinutil, MeineApp aus module de.meinefirma.meineapp
    
  5. Beachten Sie:

Analyse der Jar-Libs mit print-module-descriptor und mit jdeps

  1. Lassen Sie sich die Moduldeskriptoren anzeigen:

    jar --print-module-descriptor --file=modules/de.meinefirma.meineapp-1.0.jar

    jar --print-module-descriptor --file=modules/de.meinefirma.meinutil-1.0.jar

  2. Überprüfen Sie, dass Guava keinen Moduldeskriptor hat:

    jar --print-module-descriptor --file=modules/guava-19.0.jar

  3. Analysieren Sie die Dependencies von Guava mit jdeps:

    jdeps modules/guava-19.0.jar



Jigsaw-Demo mit doppelten Klassen und verschiedenen Modularten

Diese Demo soll zum Experiemtieren einladen:

Anlage der Verzeichnisstruktur und Erzeugen der Java-Dateien

  1. Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:

    cd \MeinWorkspace

    mkdir JigsawDuplicateVersions

    cd JigsawDuplicateVersions

    mkdir src\meinutilv1\de\meinefirma\meinutil

    mkdir src\meinutilv2\de\meinefirma\meinutil

    mkdir src\meinutilv2\de\meinefirma\extrapackage

    mkdir src\meinv3util\de\meinefirma\meinutil

    mkdir src\de.meinefirma.meinv4util\de\meinefirma\meinutil

    mkdir src\de.meinefirma.meinv5util\de\meinefirma\meinutil

    mkdir src\de.meinefirma.meineapp\de\meinefirma\meineapp

    tree /F

  2. Erstellen Sie folgende drei Jigsaw-Moduldeskriptordateien:

    Im src\de.meinefirma.meinv4util-Verzeichnis: module-info.java

    module de.meinefirma.meinv4util
    {
       exports de.meinefirma.meinutil;
    }
    

    Im src\de.meinefirma.meinv5util-Verzeichnis: module-info.java

    module de.meinefirma.meinv5util
    {
       exports de.meinefirma.meinutil;
    }
    

    Im src\de.meinefirma.meineapp-Verzeichnis: module-info.java

    module de.meinefirma.meineapp
    {
       requires meinv3util;
    }
    
  3. Erstellen Sie folgende acht Java-Klassen:

    Im src\meinutilv1\de\meinefirma\meinutil-Verzeichnis: MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static void main( String[] args ) {
          System.out.println( "\n---- Version 1, MeinUtil.class aus:\n     " +
             MeinUtil.class.getClassLoader().getResource( "de/meinefirma/meinutil/MeinUtil.class" ) );
       }
    }
    

    Im src\meinutilv2\de\meinefirma\meinutil-Verzeichnis: MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static void main( String[] args ) {
          System.out.println( "\n---- Version 2, MeinUtil.class aus:\n     " +
             MeinUtil.class.getClassLoader().getResource( "de/meinefirma/meinutil/MeinUtil.class" ) );
       }
    }
    

    Im src\meinutilv2\de\meinefirma\meinutil-Verzeichnis: ExtraUtil.java

    package de.meinefirma.meinutil;
    
    public class ExtraUtil
    {
       public static String test() { return "     de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann verwendet werden."; }
    }
    

    Im src\meinutilv2\de\meinefirma\extrapackage-Verzeichnis: ExtraKlasse.java

    package de.meinefirma.extrapackage;
    
    public class ExtraKlasse
    {
       public static String test() { return "     de.meinefirma.extrapackage.ExtraKlasse aus meinutilv2 kann verwendet werden."; }
    }
    

    Im src\meinv3util\de\meinefirma\meinutil-Verzeichnis: MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static void main( String[] args ) {
          System.out.println( "\n---- Version 3, MeinUtil.class aus: " + MeinUtil.class.getModule() );
          try {
             System.out.println( ExtraUtil.test() );
          } catch( NoClassDefFoundError ex ) {
             System.out.println( "     de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht direkt verwendet werden (" +
                                       ex.getClass().getSimpleName() + ")." );
          }
          try {
             Class<?> clss = MeinUtil.class.getClassLoader().loadClass( "de.meinefirma.meinutil.ExtraUtil" );
             System.out.println( ((ExtraUtil) clss.getConstructor().newInstance()).test() );
          } catch( ReflectiveOperationException ex ) {
             System.out.println( "     de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht per Reflection verwendet werden (" +
                                       ex.getClass().getSimpleName() + ")." );
          }
          try {
             System.out.println( de.meinefirma.extrapackage.ExtraKlasse.test() );
          } catch( NoClassDefFoundError ex ) {
             System.out.println( "     de.meinefirma.extrapackage.ExtraKlasse aus meinutilv2 kann nicht verwendet werden (" +
                                       ex.getClass().getSimpleName() + ")." );
          }
       }
    }
    

    Im src\de.meinefirma.meinv4util\de\meinefirma\meinutil-Verzeichnis: MeinUtil.java

    package de.meinefirma.meinutil;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.Enumeration;
    
    public class MeinUtil
    {
       public static void main( String[] args ) {
          System.out.println( "\n---- Version 4, MeinUtil.class aus: " + MeinUtil.class.getModule() );
    
          try {
             System.out.println( "\n---- ClassLoader().getResources(MeinUtil): " );
             Enumeration<URL> urls = MeinUtil.class.getClassLoader().getResources( "de/meinefirma/meinutil/MeinUtil.class" );
             while( urls.hasMoreElements() ) {
                System.out.println( "     " + urls.nextElement() );
             }
             System.out.println( "\n---- CLASSPATH: " + System.getProperty( "java.class.path" ) );
          } catch( IOException ex ) {
             System.out.println (ex );
          }
       }
    }
    

    Im src\de.meinefirma.meinv5util\de\meinefirma\meinutil-Verzeichnis: MeinUtil.java

    package de.meinefirma.meinutil;
    
    public class MeinUtil
    {
       public static void main( String[] args ) {
          System.out.println( "\n---- Version 5, MeinUtil.class aus: " + MeinUtil.class.getModule() );
       }
    }
    

    Im src\de.meinefirma.meineapp\de\meinefirma\meineapp-Verzeichnis: MeineApp.java

    package de.meinefirma.meineapp;
    
    import de.meinefirma.meinutil.MeinUtil;
    
    public class MeineApp
    {
       public static void main( String[] args ) {
          MeinUtil.main( null );
       }
    }
    
  4. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):

    [\MeinWorkspace\JigsawDuplicateVersions]
     '- [src]
         |- [de.meinefirma.meineapp]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meineapp]
         |   |           '- MeineApp.java
         |   '- module-info.java
         |- [de.meinefirma.meinv4util]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meinutil]
         |   |           '- MeinUtil.java
         |   '- module-info.java
         |- [de.meinefirma.meinv5util]
         |   |- [de]
         |   |   '- [meinefirma]
         |   |       '- [meinutil]
         |   |           '- MeinUtil.java
         |   '- module-info.java
         |- [meinutilv1]
         |   '- [de]
         |       '- [meinefirma]
         |           '- [meinutil]
         |               '- MeinUtil.java
         |- [meinutilv2]
         |   '- [de]
         |       '- [meinefirma]
         |           |- [extrapackage]
         |           |   '- ExtraKlasse.java
         |           '- [meinutil]
         |               |- ExtraUtil.java
         |               '- MeinUtil.java
         '- [meinv3util]
             '- [de]
                 '- [meinefirma]
                     '- [meinutil]
                         '- MeinUtil.java
    

Kompilieren und Bauen der sechs Module

  1. Aktivieren Sie Java 9.

  2. Löschen Sie Ergebnisse vorheriger Versuche und erstellen Sie zwei Verzeichnisse:

    rd /S /Q classes

    rd /S /Q modules

    rd /S /Q libs

    mkdir modules

    mkdir libs

  3. Kompilieren und Bauen Sie zuerst im libs-Verzeichnis zwei Libs für das "Unnamed Module":

    javac -d classes\meinutilv1 src\meinutilv1\de\meinefirma\meinutil\*.java

    jar --create --file=libs/meinutilv1.jar --main-class=de.meinefirma.meinutil.MeinUtil -C classes/meinutilv1 .

    javac -d classes\meinutilv2 src\meinutilv2\de\meinefirma\meinutil\*.java src\meinutilv2\de\meinefirma\extrapackage\*.java

    jar --create --file=libs/meinutilv2.jar --main-class=de.meinefirma.meinutil.MeinUtil -C classes/meinutilv2 .

  4. Kompilieren und Bauen Sie im modules-Verzeichnis ein "Automatic Module" und drei "Application Modules":

    javac -cp libs/* -d classes\meinv3util src\meinv3util\de\meinefirma\meinutil\*.java

    jar --create --file=modules/meinv3util.jar --main-class=de.meinefirma.meinutil.MeinUtil -C classes/meinv3util .

    javac -d classes\de.meinefirma.meinv4util src\de.meinefirma.meinv4util\*.java src\de.meinefirma.meinv4util\de\meinefirma\meinutil\*.java

    jar --create --file=modules/de.meinefirma.meinv4util.jar --main-class=de.meinefirma.meinutil.MeinUtil -C classes/de.meinefirma.meinv4util .

    javac -d classes\de.meinefirma.meinv5util src\de.meinefirma.meinv5util\*.java src\de.meinefirma.meinv5util\de\meinefirma\meinutil\*.java

    jar --create --file=modules/de.meinefirma.meinv5util.jar --main-class=de.meinefirma.meinutil.MeinUtil -C classes/de.meinefirma.meinv5util .

    javac -p modules -d classes\de.meinefirma.meineapp src\de.meinefirma.meineapp\*.java src\de.meinefirma.meineapp\de\meinefirma\meineapp\*.java

    jar --create --file=modules/de.meinefirma.meineapp.jar --main-class=de.meinefirma.meineapp.MeineApp -C classes/de.meinefirma.meineapp .

  5. Listen Sie die Ergebnis-Libs und -Module auf:

    dir libs /B

    dir modules /B

    [\MeinWorkspace\JigsawDuplicateVersions]
     |- [libs]
     |   |- [meinutilv1.jar]
     |   '- [meinutilv2.jar]
     '- [modules]
         |- [de.meinefirma.meineapp.jar]
         |- [de.meinefirma.meinv4util.jar]
         |- [de.meinefirma.meinv5util.jar]
         '- [meinv3util.jar]
    

Ausführung auf verschiedene Arten

  1. Führen Sie die Module auf unterschiedliche Art aus, so dass jede der fünf MeinUtil-Klassen ausgeführt wird:

  2. java -jar libs/meinutilv2.jar

    ---- Version 2, MeinUtil.class aus:
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/libs/meinutilv2.jar!/de/meinefirma/meinutil/MeinUtil.class
    

    (Kein Modulepath, kein Classpath, nur jar-Lib.)

  3. java -cp libs/*;modules/* de.meinefirma.meinutil.MeinUtil

    ---- Version 1, MeinUtil.class aus:
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/libs/meinutilv1.jar!/de/meinefirma/meinutil/MeinUtil.class
    

    (Kein Modulepath, nur Classpath. Alle 5 MeinUtil-Klassen liegen im Classpath. Verwendet wird MeinUtil aus meinutilv1.jar, weil am Anfang vom Classpath.)

  4. java -cp modules/*;libs/* de.meinefirma.meinutil.MeinUtil

    ---- Version 4, MeinUtil.class aus: unnamed module @7946e1f4
    
    ---- ClassLoader().getResources(MeinUtil):
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/modules/de.meinefirma.meinv4util.jar!/de/meinefirma/meinutil/MeinUtil.class
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/modules/de.meinefirma.meinv5util.jar!/de/meinefirma/meinutil/MeinUtil.class
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/modules/meinv3util.jar!/de/meinefirma/meinutil/MeinUtil.class
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/libs/meinutilv1.jar!/de/meinefirma/meinutil/MeinUtil.class
         jar:file:/D:/MeinWorkspace/JigsawDuplicateVersions/libs/meinutilv2.jar!/de/meinefirma/meinutil/MeinUtil.class
    
    ---- CLASSPATH: modules/de.meinefirma.meineapp.jar;modules/de.meinefirma.meinv4util.jar;modules/de.meinefirma.meinv5util.jar;
                                                                  modules/meinv3util.jar;libs/meinutilv1.jar;libs/meinutilv2.jar
    

    (Kein Modulepath, nur Classpath. Alle 5 MeinUtil-Klassen liegen im Classpath und werden mit ClassLoader.getResources() gefunden. Wegen geänderter Classpath-Reihenfolge wird diesmal MeinUtil aus de.meinefirma.meinv4util.jar verwendet. Obwohl de.meinefirma.meinv4util.jar einen Moduldeskriptor enthält, wird er dem "unnamed module" zugeordnet.)

  5. java -p modules -m de.meinefirma.meinv5util

    ---- Version 5, MeinUtil.class aus: module de.meinefirma.meinv5util
    

    (Kein Classpath, nur Modulepath.)

  6. java -cp modules/meinv3util.jar;libs/* de.meinefirma.meinutil.MeinUtil

    ---- Version 3, MeinUtil.class aus: unnamed module @39fb3ab6
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann verwendet werden.
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann verwendet werden.
         de.meinefirma.extrapackage.ExtraKlasse aus meinutilv2 kann verwendet werden.
    

    (Kein Modulepath, nur Classpath: Alle Public-Klassen können sich gegenseitig aufrufen, sowohl direkt als auch per Reflection.)

  7. java -cp libs/* -p modules -m meinv3util

    ---- Version 3, MeinUtil.class aus: module meinv3util
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht direkt verwendet werden (NoClassDefFoundError).
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht per Reflection verwendet werden (ClassNotFoundException).
         de.meinefirma.extrapackage.ExtraKlasse aus meinutilv2 kann verwendet werden.
    

    (Sowohl Classpath als auch Modulepath. Es wird MeinUtil aus meinv3util.jar verwendet, weil mit -m ausgewählt. Die ExtraUtil-Klasse kann weder direkt noch per Reflection verwendet werden, weil sie das selbe bereits verwendete Package de.meinefirma.meinutil verwendet. Beachtenswert ist, dass es hierzu keinen Compilierfehler, sondern nur einen Laufzeitfehler gibt.)

  8. java -cp libs/* -p modules -m de.meinefirma.meineapp

    ---- Version 3, MeinUtil.class aus: module meinv3util
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht direkt verwendet werden (NoClassDefFoundError).
         de.meinefirma.meinutil.ExtraUtil aus meinutilv2 kann nicht per Reflection verwendet werden (ClassNotFoundException).
         de.meinefirma.extrapackage.ExtraKlasse aus meinutilv2 kann verwendet werden.
    

    (Sowohl Classpath als auch Modulepath. Es wird MeinUtil aus meinv3util.jar verwendet, weil mit -m das Modul de.meinefirma.meineapp ausgewählt wurde, welches meinv3util per requires einbindet. Die ExtraUtil-Klasse kann aus demselben Grund wie eben wieder nicht verwendet werden. Die ExtraKlasse in dem anderen Package de.meinefirma.extrapackage kann dagegen verwendet werden. Beachten Sie hierzu die Aufrufreihenfolge: Das "Application Module" de.meinefirma.meineapp ruft das "Automatic Module" meinv3util auf, welches die Lib meinutilv2 vom "Unnamed Module" verwendet.)



Abhängigkeitsgraph mit Graphviz

  1. Mit dem grafischen Visualisierungstool Graphviz können Sie einfach eine grafische Darstellung der Jigsaw-Modulabhängigkeiten erzeugen. Dazu werden die Modulabhängigkeiten mit jdeps im DOT-Format gespeichert, welches Graphviz verwerten kann.

  2. Genauere Hinweise zur Installation von Graphviz sowohl für Windows als auch für Linux finden Sie unter: Installation Notes.
    Für dieses Beispiel genügt folgende einfache Installation:

  3. Sinn macht die grafische Darstellung nur, wenn Sie ein Projekt mit vielen Abhängigkeiten haben. Die obigen Beispiele haben alle zu wenige Abhängigkeiten. Trotzdem soll am vorherigen JigsawDuplicateVersions-Beispiel die Anwendung von Graphviz gezeigt werden:

  4. Sie erhalten:

    JigsawDuplicateVersions-Graphviz




Weitere Themen: andere TechDocs | OSGi | Docker
© 2016 Torsten Horn, Aachen