Suche
Teil 3: Top-down- und Bottom-up-Migration

Die Tücken der Migration auf Java 9

Alexandru Jecan

©Shutterstock/James W. Thompson

In diesem Artikel beschreibe ich, wie man vorhandene Java-Applikationen auf Java 9 migrieren kann und welche Schwierigkeiten und Probleme damit auftreten können. Man bedenke: Wenn man über Migration redet, muss man klar zwischen der Migration von Applikationen und der Migration von Bibliotheken unterscheiden.

Die Migration von Applikationen bezieht sich auf die Migration unseres Quellcodes. Die Migration von Bibliotheken bezieht sich auf die externen Bibliotheken, die unsere Applikation verwendet. Darüber hinaus gibt zwei unterschiedliche Arten von Migration: Top-down- und Bottom-up-Migration. Bei der Top-down-Migration werden die JAR-Dateien direkt auf dem Modulpfad platziert. Damit werden aus den JAR-Dateien automatische Module erzeugt. Anschließend muss man für jedes automatische Modul den Moduldeskriptor module-info.java anpassen und gegebenenfalls eine requires-Direktive einfügen. Schließlich lässt sich die ganze Applikation kompilieren und ausführen. Bottom-up-Migration funktioniert anders.

Mit jdeps kann man herausfinden, welche Abhängigkeiten eine JAR-Datei des Klassenpfads hat. Nachher wird die Option –generate-module-info von jdeps verwendet, um für jede einzelne JAR-Datei einen Moduldeskriptor module-info.java automatisch zu generieren. Anschließend werden die Moduldeskriptoren überprüft. Falls nötig, werden exports-Direktiven manuell entfernt, um die interne Implementierung zu verbergen. Am Schluss wird die ganze Anwendung kompiliert und ausgeführt. Bei der Ausführung muss man trotzdem beachten, dass die neue Option –add-modules eingesetzt wird, um das Root-Modul zu nennen, damit der Auflösungsprozess gestartet wird. Der Zweck des Auflösungsprozesses besteht darin, alle rekursiven Abhängigkeiten zwischen Modulen zu finden.

Jede neue Java-SE-Version führt einige Inkompatibilitäten mit früheren Releases ein. Die Modularisierung der Java-SE-Plattform bringt viele Vorteile, aber auch viele Änderungen. Laut Oracle sollte Code, der nur APIs der offiziellen Java-SE-Plattform und unterstützte JDK-spezifische APIs verwendet, weiterhin ohne Änderungen funktionieren. Code, der bestimmte Funktionen oder JDK-interne APIs verwendet, kann nicht ausgeführt werden oder unterschiedliche Ergebnisse liefern.

Artikelserie

Automatische Module sind der Schlüssel

Die automatischen Module werden für die Migration bestehender Anwendungen auf Java 9 verwendet. Ein automatisches Modul ist ein echtes Modul, das erstellt wird, indem man eine JAR-Datei auf den Modulpfad legt. Das geschieht mit der Option –module-path. Das automatische Modul erfordert (requires) alle vorhandenen Module aus dem System. Das sind alle eigenen Module, alle Module aus dem JDK sowie alle anderen automatische Module. Das heißt, ein automatisches Modul liest alle diese Module. Außerdem exportiert ein automatisches Modul alle Packages. Ein automatisches Modul kann zudem auf Typen des Klassenpfads zugreifen und eignet sich besonders für Drittanbietercode. Es wird nicht explizit deklariert. Stattdessen wird es automatisch beim Legen einer JAR-Datei auf den Modulpfad erstellt. Wir müssen keine Änderungen an der JAR-Datei vornehmen.

Der Name des automatischen Moduls wird aus dem Namen der JAR-Datei abgeleitet. Die Entwickler sind damit nicht gezwungen abzuwarten, bis alle Drittanbieter ihre Bibliotheken modularisieren. Mithilfe des neuen Konzepts ist es möglich, JAR-Dateien schnell zu modularisieren und als Module auf dem Modulpfad statt als JAR-Dateien auf dem Klassenpfad zu verwenden. Natürlich es ist möglich, diese JAR-Dateien weiter auf dem Klassenpfad zu nutzen. In diesem Fall werden sie nicht modularisiert.

Mit jdeps interne JDK-APIs aufspüren

Das Tool Java Dependency Analysis (jdeps) ist ein Werkzeug, um alle statischen Abhängigkeiten einer Bibliothek zu entdecken. Es wurde in Java 8 eingeführt, aber in Java 9 wurde es mit einigen nützlichen neuen Optionen und Features erweitert. jdeps führt eine statische Analyse der JAR-Dateien durch. Es verfügt über die Option -jdkinternals, um Abhängigkeiten zu allen nicht unterstützten JDK-internen APIs zu finden. Das Tool ist im Allgemeinen sehr nützlich, wenn man sich für die Umstellung auf Java 9 vorbereitet. Wir können überprüfen, ob unsere vorhandenen JAR-Dateien aus dem Klassenpfad JDK-interne APIs enthalten. Indem wir Ersatz für die internen JDK-APIs einplanen, wird der Übergang zu Java 9 einfacher. Wir zeigen ein Beispiel für die Verwendung von jdeps mit der Option –jdkinternals mit der Spark-Bibliothek 2.10. In Listing 1 wird geprüft, welche JDK-internen APIs sie enthält.

$ jdeps -jdkinternals spark-core_2.10-2.1.0.jar

spark-core_2.10-2.1.0.jar -> JDK removed internal API

spark-core_2.10-2.1.0.jar -> java.base

spark-core_2.10-2.1.0.jar -> jdk.unsupported
  org.apache.spark.serializer.SerializationDebugger$
-> sun.security.action.GetBooleanAction
JDK internal API (java.base)

  org.apache.spark.storage.StorageUtils$
-> sun.misc.Cleaner
JDK internal API (JDK removed internal API)

  org.apache.spark.storage.StorageUtils$
-> sun.nio.ch.DirectBuffer
JDK internal API (java.base)

  org.apache.spark.util.ClosureCleaner$
-> sun.reflect.ReflectionFactory
JDK internal API (jdk.unsupported)

  org.apache.spark.util.SignalUtils$$anonfun$1
-> sun.misc.Signal
JDK internal API (jdk.unsupported)

  org.apache.spark.util.SignalUtils$ActionHandler
-> sun.misc.Signal
JDK internal API (jdk.unsupported)

  org.apache.spark.util.SignalUtils$ActionHandler
-> sun.misc.SignalHandler
JDK internal API (jdk.unsupported)

  org.apache.spark.util.SignalUtils$ActionHandler$$anonfun$2
-> sun.misc.Signal
JDK internal API (jdk.unsupported)

  org.apache.spark.util.SignalUtils$ActionHandler$$anonfun$3
-> sun.misc.Signal
JDK internal API (jdk.unsupported)

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 dependence 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.misc.Cleaner                        Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
sun.misc.Signal                         See http://openjdk.java.net/jeps/260
sun.misc.SignalHandler                  See http://openjdk.java.net/jeps/260
sun.reflect.ReflectionFactory           See http://openjdk.java.net/jeps/260
sun.security.action.GetBooleanAction    Use java.security.PrivilegedAction @since 1.1

Die Spark-Bibliothek enthält fünf JDK-interne APIs, die von jdeps entdeckt wurden. Für sun.misc.Signal, sun.misc.SignalHandler und sun.reflect.ReflectionFactory gibt jdeps keine Hinweise, was mögliche Möglichkeiten für den Ersatz betrifft. Der Grund liegt darin, dass diese APIs in JDK 9 zugänglich bleiben, zusammen mit sun.misc.Unsafe. Dieses kritische interne API wurde im Modul jdk.unsupported hinzugefügt. Dieses Modul ist in jedem JDK-Image vorhanden, die APIs sind daher für Code auf dem Klassenpfad zugänglich. Zudem werden diese APIs auch für andere Module zugänglich sein, unter der Voraussetzung, dass es eine Abhängigkeit zwischen den jeweiligen Modulen und dem Modul jdk.unsupported besteht. Laut Oracle wird das kritische interne API im JDK 9 als deprecated gelten. Im JDK 10 werden sie entweder gekapselt oder entfernt. jdeps verfügt über eine kleine Datenbank, die eine Zuordnung zu unterstützten oder neueren APIs bereitstellt. Das Tool erkennt, wenn es einen möglichen Standard- oder JDK-spezifischen Ersatz dafür gibt.

Den Moduldeskriptor automatisch generieren

jdeps kann in Java 9 verwendet werden, um automatisch einen Moduldeskriptor module-info.java zu generieren. Dafür wird die jdeps-Option –generate-module-info verwendet. Die Syntax sieht wie folgt aus:

jdeps --generate-module-info <Output_Verzeichnis>  <Liste_von_JAR_Dateien>

Wir können eine oder mehrere JAR-Dateien eingeben. Als Ergebnis wird für jede JAR-Datei ein Moduldeskriptor module-info.java generiert. Wenn wir eine JAR-Datei verwenden, die externe Abhängigkeiten hat, müssen wir auch die abhängigen JAR-Dateien übergeben, damit die Moduldeskriptoren erfolgreich generiert werden. jdeps generiert eine exports-Klausel für jedes einzelne Package in der JAR-Datei. Für Abhängigkeiten führt jdeps eine Analyse der Signaturen der Typen der public-Methoden in den JAR-Dateien durch. Wenn es merkt, dass es einen Public Type für ein anderes Modul gibt, generiert jdeps die entsprechende requires transitive-Klausel. Das heißt, jdeps wird einen ziemlich großen module-info.java-Moduldeskriptor generieren. Der Grund liegt darin, dass jdeps nicht weiß, welche die exported Packages beziehungsweise die internen Packages sind.

Im nächsten Beispiel werden wir mithilfe von jdeps eine module-info.java-Datei für JUnit generieren. Da JUnit von Hamcrest Core abhängt, müssen wir auch die hamcrest-core.jar-Datei übergeben. Wir führen den folgenden Befehl aus:

Jdeps --generate-module-info target hamcrest-core-1.3.jar junit-4.12.jar

Das Ergebnis ist:

writing to target\junit\module-info.java
writing to target\hamcrest.core\module-info.java

Es wurden also zwei module-info.java-Moduldeskriptoren von jdeps generiert. In Listing 2 schauen wir uns einen Auszug aus dem module-info.java von JUnit an.

module junit {
  requires transitive hamcrest.core;
  requires java.management;
  exports junit.extensions;
  exports junit.framework;
  exports junit.runner;
  exports junit.textui;
  exports org.junit;
  exports org.junit.experimental;
  ...
  exports org.junit.internal;
  ...
  exports org.junit.matchers;
  exports org.junit.rules;
  exports org.junit.runner;
  exports org.junit.runner.manipulation;
  exports org.junit.runner.notification;
  exports org.junit.runners;
  exports org.junit.runners.model;
  exports org.junit.runners.parameterized;
  exports org.junit.validator;
}

Die automatisch generierte module-info.java-Datei enthält eine requires transitive hamcrest.core-Klausel, die angibt, dass JUnit eine Abhängigkeit zum Hamcrest Core hat. Zusätzlich enthält die module-info.java-Datei die exports-Klausel für jedes Package. Die module-info.java-Datei von Hamcrest Core sieht folgendermaßen aus:

module hamcrest.core {
  exports org.hamcrest;
  exports org.hamcrest.core;
  exports org.hamcrest.internal;
}

Das Modul hamcrest.core hat keine Abhängigkeiten und exportiert alle Packages.

Eine Applikation modularisieren

Eine Applikation kann weiterhin ohne Probleme in Java 9 kompiliert und ausgeführt werden, wenn sie keine JDK-internen APIs enthält. In Listing 3 haben wir eine einfache Klasse, die nicht Teil eines Moduls ist und das SLF4J-API verwendet.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(Main.class);
    logger.info("Hello World");
  }
}

Wir können diese Klasse ohne Probleme mit JDK 9 kompilieren und ausführen. Im nächsten Beispiel verwenden wir den Klassenpfad:

export CLASSPATH=".;library\slf4j-api-1.7.25.jar;library\slf4j-simple-1.7.25.jar"

javac -cp $CLASSPATH -d out -sourcepath src $(find src -name '*.java')

java -cp "$CLASSPATH;out" Main

Dieses Beispiel hat reibungslos funktioniert, da wir bisher keine JDK-internen APIs eingesetzt haben. Wie schon erwähnt, sind die meisten JDK-internen APIs in Java 9 nicht mehr zugänglich. Wir ändern unsere Klasse Main und fügen ein JDK-internes API hinzu: sun.net.URLCanonicalizer. Wenn wir die Main-Klasse nochmals mit dem gleichen javac-Befehl kompilieren, erhalten wir den folgenden Fehler:

src\Main.java:3: error: package sun.net is not visible
import sun.net.URLCanonicalizer;
  (package sun.net is declared in module java.base, which does not export it to the unnamed module)
1 error

Der URLCanonicalizer des Packages sun.net ist nicht sichtbar, weil er in Java 9 gekapselt ist. Um ein JDK-internes API in Java 9 zugänglich zu machen, können wir die Option –add-exports verwenden. Die Syntax der Option –add-exports sieht so aus:

--add-exports<Name_von_Modul_1>/<Name_von_Package>=<Name_von_Modul_2>

Die Option –add-exports stellt sicher, dass das Package des Modul_1 in Modul_2 zugänglich wird. Wir kompilieren unseren Code mit dem folgenden Befehl:

javac -cp $CLASSPATH -d out --add-exports java.base/sun.net=ALL-UNNAMED -sourcepath src $(find src -name '*.java')

Die Konstante ALL-UNNAMED entspricht dem ganzen Quellcode des Klassenpfads. Das heißt, dass das Package sun.net vom Modul java.base für den ganzen Quellcode des Klassenpfads zugänglich sein wird. Wir können in diesem Fall kein Modul spezifizieren, weil unser Code noch nicht modularisiert ist. In diesem Fall ist die Kompilierung erfolgreich, da wir wieder auf das interne JDK-API des Packages sun.net zugreifen können.

So lässt sich der Modulpfad einsetzen

Nun möchten wir unsere Applikation modularisieren. Dafür erstellen wir ein Modul namens com.javamagazin.moduleA und kopieren unsere Main-Klasse in dieses neue Modul. Nicht vergessen: Wir müssen auch einen module-info.java-Moduldeskriptor für das neue Modul erstellen. Die module-info.java-Datei ist am Anfang leer:

module com.javamagazin.moduleA {
}

Wir werden die SLF4J-Bibliothek als automatisches Modul verwenden. Wie vorher erläutert, müssen wir dafür einfach die JAR-Datei auf den Modulpfad setzen. Daraus wird ein automatisches Modul mit dem Namen slf4j.api. Da wir jetzt ein Modul haben und nicht mehr mit dem Klassenpfad arbeiten, sollen wir das JDK-interne API nicht mehr für den Klassenpfad zugänglich machen, sondern für unser Modul com.javamagazin.moduleA. Dafür werden wir für unsere Option –add-exports die Konstante ALL-UNNAMED mit dem Namen unseres Moduls com.javamagazin.moduleA ersetzen, da das Package sun.net künftig in unserem Modul zugänglich sein muss. Wir kompilieren und verwenden auch die Option –module-path:

javac -d modules --module-path "automaticModules" 
--add-exports java.base/sun.net=com.javamagazin.moduleA 
--module-source-path src $(find src -name "*.java")

Die Option –module-path gibt an, dass wir die JAR-Dateien aus dem Verzeichnis automaticModules auf den Modulpfad setzen wollen. Das Verzeichnis automaticModules enthält die slf4j-api-1.7.25.jar– und slf4j-simple-1.7.25.jar-Dateien. Bei der Kompilierung werden diese JAR-Dateien sozusagen als automatische Module betrachtet. Das Ergebnis der Kompilierung sieht wie folgt aus:

src\com.javamagazin.moduleA\com\javamagazin\moduleA\Main.java:3: error: package org.slf4j is not visible
import org.slf4j.Logger;
  (package org.slf4j is declared in the unnamed module, but module org.slf4j does not read it)
src\com.javamagazin.moduleA\com\javamagazin\moduleA\Main.java:4: error: package org.slf4j is not visible
import org.slf4j.LoggerFactory;

Wir haben das automatische Modul namens org.slf4j erfolgreich erstellt. Aber wir müssen den Moduldeskriptor explizit anweisen, das neue automatische org.slf4j-Modul zu erfordern, da wir in unserem Code eine Abhängigkeit zu diesem Modul haben:

module com.javamagazin.moduleA {
  requires slf4j.api;
}

Die Kompilierung ist erfolgreich, wenn wir wieder mit dem gleichen Befehl kompilieren. Als Nächstes möchten wir unsere Applikation ausführen. Wir haben aktuell zwei Module: ein Modul com.javamagazin.moduleA im Verzeichnis modules und ein automatisches Modul im Verzeichnis automaticModules. Im automaticModules-Verzeichnis befinden sich weiterhin nur die JAR-Dateien. Beide Verzeichnisse müssen wir angeben, wenn wir die Applikation mit dem Java-Werkzeug ausführen:

java --module-path "automaticModules;modules" 
--add-exports java.base/sun.net=com.javamagazin.moduleA 
-m com.javamagazin.moduleA/com.javamagazin.moduleA.Main

Unsere Applikation wird damit erfolgreich ausgeführt.

Probleme bei der Migration

Ein Problem bei der Migration hat mit der Tatsache zu tun, dass nicht alle Plattformmodule, die sich im JDK befinden, aus der Sicht des Applikationsklassenpfads zur Kompilierzeit zur Verfügung stehen. Das sind die so genannten ungelösten Plattformmodule. Wir kennen den Graphen der Module schon. Abbildung 1 zeigt nur den oberen Teil des Graphen.

Abb. 1: Oberer Teil des Modulgraphen

Abb. 1: Oberer Teil des Modulgraphen

Ganz oben steht das Modul java.se.ee. Das Modul java.se befindet sich eine Stufe darunter. Laut Oracle sammelt das Modul java.se.ee alle Module zusammen, die die Java-SE-Plattform umfassen, einschließlich der Module, die mit der Java-EE-Plattformspezifikation überlappen. Das java.se-Aggregator-Modul sammelt die Teile der Java-SE-Plattform zusammen, die nicht mit Java EE überlappen. Zum Zeitpunkt der Kompilierung, wenn wir Code auf dem Klassenpfad mit Java 9 kompilieren, wird Jigsaw das Modul java.se als Wurzelmodul aller sichtbaren Module nehmen (und nicht das Modul java.se.ee). Als Ergebnis werden die Packages, die sich nur im Modul java.se.ee befinden (und nicht im Modul java.se), nicht auffindbar sein. Das heißt, wenn wir ein Package aus dem Modul java.se.ee verwenden, werden wir den folgenden Error bekommen: Package does not exist. Aber welche sind die Packages, die im Modul java.se.ee vorhanden sind und nicht automatisch aufgelöst werden? Die Packages aus dem Modul java.se.ee sind:

  • xml.ws
  • xml.bind
  • transaction
  • corba
  • activation
  • annotations.common

Alle diese sechs Packages werden standardmäßig nicht aufgelöst. Wir müssen sie explizit als Module hinzufügen, sowohl während der Kompilierungszeit als auch während der Laufzeit mithilfe der Option –add-modules. Um beispielsweise die Packages aus java.activation hinzuzufügen, damit sie vom System auffindbar sind, müssen wir sowohl während der Kompilierungszeit als auch während der Laufzeit die Option –add-modules java.activation zusätzlich aufrufen. Die Option –add-modules <Name_von_Modul> wird verwendet, um dem Root-Set Module hinzuzufügen, und sicherzustellen, dass spezifische Plattform-, Bibliothek- oder Service-Provider-Module im Modulgraphen vorhanden sind. Diese Option fügt praktisch die benannten Module im Modulgraphen hinzu.

JDK-interne APIs sind ein Problempunkt

Wenn wir JDK-interne APIs verwenden, wird unsere Applikation schon während der Kompilierung scheitern. Nur ein paar JDK-interne APIs sind in Java 9 noch zugänglich, aber der Rest nicht. Wir haben gesehen, wie man jdeps einsetzen kann, um die JDK-internen APIs zu entdecken. Außerdem, wie wir die JDK-internen APIs mithilfe der –add-exports-Option wieder zugänglich machen können. Eine Alternative besteht darin, die Verkapselung nicht mit –add-exports zu brechen, sondern manuell Schritt für Schritt jede JDK-interne Klasse mit einer anderen unterstützten Klasse zu ersetzen. Oracle wird jedoch in Zukunft – wahrscheinlich ab Java 10 – manche JDK-internen APIs komplett entfernen. Daher ist es sinnvoll, diese JDK-internen APIs so schnell wie möglich zu ersetzen.

Der vermaledeite Unterstrich

Das Unterstrich Zeichen _ kann in Java 9 nicht mehr als ein Zeichenbezeichner verwendet werden. Daher sollte man auf ihn verzichten. Wenn wir ihn trotzdem in Java 9 verwenden, wird der folgende Error geworfen:

error: as of release 9, '_' is a keyword, and may not be used as a legal  identifier

Die rt.jar-, tools.jar- und dt.jar-Dateien entfernt

Die JAR-Dateien rt.jar, tools.jar und dt.jar wurden aus dem JDK 9 komplett entfernt. Das hat Auswirkungen auf unseren Quellcode. So wird im JDK 9 z. B. der Aufruf ClassLoader::getSystemResource() keinen URL zurückgeben, der auf eine JAR-Datei verweist, da es keine JAR-Dateien mehr gibt. Stattdessen wird ein valider URL zurückgegeben. Wenn wir in Java 9 die Methode getSystemResource() mit dem Parameter java/lang/Class.class aufrufen,

ClassLoader.getSystemResource("java/lang/Class.class")

wird folgender URL zurückgegeben:

jrt:/java.base/java/lang/Class.class

Oracle hat in Java 9 sechs Methoden komplett entfernt:

  • util.logging.LogManager.addPropertyChangeListener
  • util.logging.LogManager.removePropertyChangeListener
  • util.jar.Pack200.Packer.addPropertyChangeListener
  • util.jar.Pack200.Packer.removePropertyChangeListener
  • util.jar.Pack200.Unpacker.addPropertyChangeListener
  • util.jar.Pack200.Unpacker.removePropertyChangeListener

Diese Methoden können in Java 9 nicht mehr verwendet werden. Alter Quellcode, der die Methoden nutzt, sollte angepasst werden.

Neues Format für die String-Version

In Java 9 gibt ein neues Format für die Versionen. Wenn unser Code auf dem Version-String-Format basiert, um zwischen Versionen zu unterscheiden, müssen wir möglicherweise den Code aktualisieren. Das Format der neuen String-Version ist:

$MAJOR.$MINOR.$SECURITY.$PATCH

Laut der offiziellen Dokumentation von Oracle würde das Java-9u5-Release unter dem alten Schema die String-Version 1.9.0_5-b20 haben. Unter dem neuen Schema ist die kurze Version 9.0.1 und die lange Version 9.0.1+20. Diese Änderung wirkt sich auf java-version und auf verwandte Systemeigenschaften aus, wie java.runtime.version, java.vm.version, java.specification.version und java.vm.specification.version.

Änderungen an Reflection

Um Deep Reflection auf einem Package durchführen zu können, müssen wir diese spezifisch deklarieren. Standardmäßig ist Deep Reflection in Java 9 ausgeschaltet. Um Deep Reflection zu ermöglichen, sollten wir die so genannten Open-Module verwenden, die in Java 9 hinzugefügt wurden. Ein Open-Modul macht alle Packages für Deep Reflection verfügbar. Die Packages aus einem Open-Modul sind aber zur Kompilierungszeit nicht zugänglich. Sie sind nur für Deep Reflection verfügbar, damit man z. B. die Methode setAccessible() aufrufen kann. Die Open-Module sind nützlich, da viele Frameworks von Drittanbietern auf Basis von Reflection auf verschiedene Java-Klassen zugreifen. Die Direktive opens wird innerhalb des Moduldeskriptors module-info.java verwendet, um zu spezifizieren, dass ein bestimmtes Package für Deep Reflection zugänglich sein soll. Diese Direktive wird verwendet, wenn keine Abhängigkeit zur Kompilierungszeit erforderlich ist.

Fazit

Für die Migration einer Anwendung auf Java 9 sind mehrere Ansätze möglich. Der Entwickler hat die Freiheit, den besten Ansatz auszuwählen und einzusetzen. Unsere Empfehlung ist, zuerst sicherzustellen, dass die Applikation nur mit dem Klassenpfad gut funktioniert. Dann kann man in Schritt 2 ein einziges Modul erstellen, das die ganze Applikation umfasst. Wenn die Applikation richtig läuft und Fehler behoben wurden, kann man zum nächsten Schritt gehen und anfangen, die Applikation komplett zu modularisieren. Dafür werden mehrere Module erzeugt und die Abhängigkeiten zwischen den Modulen in den Moduldeskriptoren definiert. Wenn unsere Anwendung über externe Bibliotheken verfügt, ist es sinnvoll, mit den externen Bibliotheken anzufangen und diese in Schritten zu modularisieren. Die externen Bibliotheken werden dann automatische Module.

Während der Migration ist es wichtig, darauf zu achten, nicht mehr zu exportieren als überhaupt nötig. Wir müssen auch gut unterscheiden, wann wir eine exports-Klausel brauchen und wann wir lediglich eine opens-Klausel brauchen. Falls wir nur Zugang auf Basis von Deep Reflection brauchen, reicht eine opens-Klausel. Man sollte auch gut unterscheiden, wann wir eine direkte Abhängigkeit und wann eine Laufzeitabhängigkeit haben. Die direkten Abhängigkeiten sollte man mit der Requires-Klausel spezifizieren. Die Laufzeitabhängigkeiten sollen mit der Option –add-modules spezifiziert werden.

Mehr zum Thema:

Java 9 Tutorial: Erstellen von modularen Laufzeit-Images mit jlink

Verwandte Themen:

Geschrieben von
Alexandru Jecan
Alexandru Jecan
Alexandru Jecan ist Diplom-Informatiker und hat mehr als sechs Jahre Erfahrung im IT-Bereich mit Fokus auf Architektur, Softwareentwicklung und Qualitätssicherung von Enterprise-, Web-, Client-Server- und Mobile-Anwendungen.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.