Hinweis:

Erste Schritte mit GraalVM Native Image

Einführung

In dieser Übung erstellen Sie eine cloud-native Java-Anwendung mit GraalVM Native Image. Sie richtet sich an Entwickler mit Java-Kenntnissen.

Die GraalVM Native Image-Technologie kompiliert Java-Code im Voraus in eine eigenständige ausführbare Datei. Nur der Code, der zur Laufzeit von der Anwendung benötigt wird, wird der ausführbaren Datei hinzugefügt.

Eine ausführbare Datei, die von Native Image erstellt wird, bietet mehrere wichtige Vorteile:

Viele der führenden Microservice-Frameworks unterstützen die Kompilierung im Voraus mit GraalVM Native Image, einschließlich Micronaut, Spring, Helidon und Quarkus.

Darüber hinaus gibt es Maven- und Gradle-Plug-ins für Native Image, damit Sie Java-Anwendungen einfach als ausführbare Dateien erstellen, testen und ausführen können.

Hinweis: Oracle Cloud Infrastructure (OCI) stellt GraalVM Enterprise ohne zusätzliche Kosten bereit.

Geschätzte Labordauer: 45 Minuten

Übungsziele

In dieser Übung führen Sie die folgenden Aufgaben aus:

HINWEIS: Wenn das Laptopsymbol in der Übung angezeigt wird, müssen Sie z.B. einen Befehl eingeben. Achten Sie darauf.

# This is where we you will need to do something

Schritt 1: Verbindung zu einem Remotehost herstellen und Entwicklungsumgebung prüfen

Ihre Entwicklungsumgebung wird von einem Remotehost bereitgestellt: einer OCI Compute-Instanz mit Oracle Linux 8, 1 CPU und 32 GB Arbeitsspeicher.
Die Luna Labs-Desktopumgebung wird angezeigt, bevor der Remote-Host bereit ist. Dieser Vorgang kann bis zu zwei Minuten dauern.

Die Verbindung zu Ihrem Remote-Host wird über ein Setup-Skript in der Luna Desktop-Umgebung hergestellt. Dieses Skript ist über die Registerkarte "Ressourcen" verfügbar.

  1. Doppelklicken Sie auf dem Desktop auf das Luna-Lab.html-Symbol. Die Seite wird geöffnet, auf der Oracle Cloud Infrastructure-Zugangsdaten und spezifische Informationen für Ihre Übung angezeigt werden.

  2. Die Registerkarte Ressourcen wird angezeigt. Die neben dem Titel Ressourcen angezeigte Kopie wird gestartet, während die Compute-Instanz in der Teh-Cloud bereitgestellt wird.

  3. Wenn die Instanz bereitgestellt ist, kann dies bis zu 2 Minuten dauern. Auf der Registerkarte Ressourcen wird Folgendes angezeigt:

    Luna Resources (Registerkarte)

  4. Kopieren Sie das Konfigurationsskript, mit dem die VS-Codeumgebung eingerichtet wird, über die Registerkarte "Ressourcen". Klicken Sie auf den Link Details anzeigen, um die Konfiguration anzuzeigen. Kopieren Sie dies wie im Screenshot unten dargestellt.

    Konfigurationsskript kopieren

  5. Öffnen Sie ein Terminal, wie im folgenden Screenshot gezeigt:

    Terminal öffnen

  6. Fügen Sie den Konfigurationscode in das Terminal ein. Dadurch wird der VS-Code für Sie geöffnet.

    Terminal 1 einfügen

    Terminal 2 einfügen

Und du bist fertig! Herzlichen Glückwunsch. Sie sind jetzt erfolgreich mit einem Remotehost in Oracle Cloud verbunden.

Hinweis zur Entwicklungsumgebung

Sie verwenden GraalVM Enterprise 21 als Java-Plattform für diese Übung. GraalVM ist eine leistungsstarke JDK-Distribution von Oracle, die auf einer vertrauenswürdigen und sicheren Oracle Java SE basiert.

Ihre Entwicklungsumgebung ist mit GraalVM und den Tools für native Images vorkonfiguriert, die für diese Übung erforderlich sind.

Sie können dies einfach prüfen, indem Sie die folgenden Befehle im Terminal ausführen: Sie können ein Terminal mit dem VS-Code Terminal > New Terminal erstellen:

java -version

native-image --version

Schritt 2: Demoanwendung erstellen und ausführen

In einer Demoanwendung wird ein GraalVM Native Image dargestellt: eine Befehlszeilen-Java-Anwendung, die die Anzahl von Dateien im aktuellen Verzeichnis und in den zugehörigen Unterverzeichnissen zählt. Als nettes Extra berechnet die Anwendung auch die Gesamtgröße der Dateien.

Der Quellcode für die Anwendung ist auf Ihrem Remotehost verfügbar.

Hinweis zur Demoanwendung

Die Anwendung besteht aus zwei Java-Dateien, die sich im Verzeichnis src befinden:

Die Anwendung kann manuell oder mit Maven-Profilen erstellt werden. Die Maven-Build-Konfiguration wird von der Datei pom.xml bereitgestellt. Maven-Profile sind eine hervorragende Möglichkeit, verschiedene Build-Konfigurationen in einer einzelnen pom.xml-Datei zu haben. Weitere Informationen zu Maven-Profilen finden Sie hier.

Sie können die Dateien im geöffneten VS-Code durchsuchen.

In dieser Übung werden mehrere Profile verwendet, die jeweils einen bestimmten Zweck haben:

  1. native: Dieses Profil erstellt eine ausführbare Datei mit GraalVM Native Image.
  2. java_agent: Dieses Profil erstellt die Java-Anwendung mit einem Tracing Agent, der alle Verwendungen des dynamischen Codes in Ihrer Anwendung verfolgt und diese Informationen in Konfigurationsdateien erfasst. Mehr dazu später.

Sie verwenden ein bestimmtes Maven-Profil, indem Sie es als Parameter an den Befehl mvn übergeben. Der Name des Profils wird an das Kennzeichen -P angehängt. Sie können die folgenden Befehle innerhalb des Terminals im VS-Code ausführen.

Das folgende Beispiel zeigt, wie Sie beim Erstellen mit Maven ein native-Profil aufrufen können:

mvn clean package -Pnative

Jetzt haben Sie grundlegende Kenntnisse darüber, was die Anwendung ausführt, um zu sehen, wie sie funktioniert.

  1. Erstellen Sie das Projekt und führen Sie es aus dem Terminal, das wir in VS Code geöffnet haben:

    mvn clean package exec:exec
    

    Der obige Befehl führt folgende Schritte aus:

    1. Löscht das Projekt, um generierte oder kompilierte Artefakte zu entfernen.
    2. Erstellt eine ausführbare JAR-Datei, die die Anwendung enthält. Diese JAR-Datei wird später von Native Image verwendet.
    3. Führt die Anwendung aus, indem das Plug-in exec ausgeführt wird.

    In der generierten Ausgabe sollte Folgendes angezeigt werden (die Anzahl der von der Anwendung gemeldeten Dateien kann variieren):

    Counting directory: .
    Total: 15 files, total size = 511.9 KiB
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    

Schritt 3: Java-Anwendung in eine ausführbare Datei umwandeln

Als Nächstes erstellen Sie eine ausführbare Version der Anwendung mit GraalVM Native Image. Zur schnellen Erinnerung: GraalVM Native Image ist eine Kompilierungstechnologie im Voraus, die Ihre Java-Anwendung in eine eigenständige ausführbare Datei konvertiert, für die kein JDK ausgeführt werden muss, schnell zu starten und effizient ist.

Das native GraalVM Image ist auf dem Remotehost vorinstalliert.

  1. Prüfen Sie zunächst, ob eine kompilierte JAR-Datei im Verzeichnis target vorhanden ist:

    ls ./target
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 archive-tmp
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 classes
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 generated-sources
    -rw-rw-r-- 1 krf krf  496273 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar
    -rw-rw-r-- 1 krf krf    7894 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT.jar
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-archiver
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-status
    

    Sie benötigen die Datei graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar.

  2. Generieren Sie eine ausführbare Datei aus der Befehlszeile. Sie müssen das Maven-Plug-in nicht zur Verwendung von GraalVM Native Image verwenden, aber es kann hilfreich sein. Führen Sie im Root-Verzeichnis des Projekts demo Folgendes aus:

    native-image -jar ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar --no-fallback -H:Class=oracle.App -H:Name=file-count
    

    Dadurch wird eine ausführbare Datei mit dem Namen file-count im aktuellen Verzeichnis generiert.

  3. Führen Sie diese ausführbare Datei wie folgt aus:

    ./file-count
    
  4. Aktuelle Anwendungszeit. Führen Sie sie zunächst als ausführbare Datei und dann mit dem regulären java-Befehl aus:

    time ./file-count
    

    time java -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    Die vom Befehl native-image generierte ausführbare Datei wird wesentlich schneller ausgeführt als die entsprechende Java-Anwendung.

Im Folgenden wird näher erläutert, wie Sie die ausführbare Datei erstellt haben.

Welche Parameter geben Sie an, die Sie in Schritt 2 an den Befehl native-image übergeben haben?

Die vollständige Dokumentation finden Sie hier.

Sie können das Tool native-image auch mit dem Maven-Plug-in für GraalVM Native Image ausführen. Die Projektdatei pom.xml (Maven-Konfiguration) enthält das folgende Snippet, das zeigt, wie eine ausführbare Datei mit dem Plug-in erstellt wird:

<!-- Native Image -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>${native.maven.plugin.version}</version>
    <extensions>true</extensions>
    <executions>
    <execution>
        <id>build-native</id>
        <goals>
        <goal>build</goal>
        </goals>
        <phase>package</phase>
    </execution>
    </executions>
    <configuration>
        <skip>false</skip>
        <imageName>${exe.file.name}</imageName>
        <mainClass>${app.main.class}</mainClass>
        <buildArgs>
            <buildArg>--no-fallback</buildArg>
            <buildArg>--report-unsupported-elements-at-runtime</buildArg>
        </buildArgs>
    </configuration>
</plugin>

Das Maven-Plug-in für Native Image übernimmt den Aufwand für die Erstellung der ausführbaren Datei. Sie können sie mit dem Tag <skip>true</skip> deaktivieren. Beachten Sie auch, dass Sie Parameter über die Tags <buildArgs/> an native-image übergeben können.

Die vollständige Dokumentation zum GraalVM Native Image-Plug-in finden Sie hier.

Um die ausführbare Datei mit dem Maven-Profil zu erstellen, führen Sie Folgendes aus:

mvn clean package -Pnative

Der Maven-Build speichert die ausführbare Datei file-count in das Verzeichnis target.

Sie können die ausführbare Datei wie folgt ausführen:

./target/file-count

Schritt 4: Reflexion verwenden - Abhängigkeit zu Log4J hinzufügen

In diesem Schritt erstellen Sie eine ausführbare Datei, die mit den dynamischen Features von Java funktioniert.

Angenommen, Sie möchten Ihrer Anwendung, die auf Reflexion basiert, eine Library oder einen Code hinzufügen. Ein guter Kandidat für das Testen von Reflexionen ist das Logging-Framework Log4J. Es wurde bereits als Abhängigkeit in der Projektdatei pom.xml hinzugefügt:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

Um die Anwendung so zu ändern, dass sie log4j verwendet, bearbeiten Sie die Datei ListDir.java, und entfernen Sie das Kommentarzeichen für einige Zeilen.

  1. Öffnen Sie die Datei ListDir.java mit dem VS-Code

  2. Heben Sie die Kommentarzeichen für die Zeile auf, die den Import log4j deklariert, und heben Sie dann die Kommentarzeichen für die folgenden Zeilen auf:

    //import org.apache.log4j.Logger;
    

    //final static Logger logger = Logger.getLogger(ListDir.class);
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + dirName);
    }
    */
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + f.getAbsolutePath());
    }
    */
    
  3. Speichern Sie die Datei

    Nachdem Sie der Anwendung ein Logging hinzugefügt haben, können Sie das Ergebnis Ihrer Änderungen anzeigen, indem Sie sie neu erstellen und ausführen.

    mvn clean package exec:exec
    

    Sie sollten dieselbe Art von Ausgabe wie zuvor sehen, aber mit dem zusätzlichen Logging.

  4. Erstellen Sie als Nächstes eine ausführbare Datei mit dem Maven-Profil:

    mvn clean package -Pnative
    
  5. Führen Sie die erstellte ausführbare Datei aus, die jetzt Logging enthält:

    ./target/file-count
    

    Dadurch wird ein Fehler generiert:

    Exception in thread "main" java.lang.NoClassDefFoundError
            at org.apache.log4j.Category.class$(Category.java:118)
            at org.apache.log4j.Category.<clinit>(Category.java:118)
            at java.lang.Class.ensureInitialized(DynamicHub.java:552)
            at oracle.ListDir.<clinit>(ListDir.java:75)
            at oracle.App.main(App.java:63)
    Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category
            at java.lang.Class.forName(DynamicHub.java:1433)
            at java.lang.Class.forName(DynamicHub.java:1408)
            ... 5 more
    

    Was ist hier passiert?

Mit den dynamischen Features von Java arbeiten

Diese Ausnahme wird durch das Hinzufügen der Log4J-Library verursacht, da sie auf Reflexion basiert.
Das Tool native-image führt eine aggressive statische Analyse durch, um festzustellen, welche Klassen in der Anwendung verwendet werden. Bei nicht verwendeten Klassen geht das Tool davon aus, dass sie nicht benötigt werden. Dies wird als "geschlossene Welt" angenommen - alles, was geladen werden muss, muss bekannt sein, wenn eine ausführbare Datei erstellt wird. Wenn sie nicht durch statische Analyse gefunden werden kann, wird sie nicht in die ausführbare Datei aufgenommen.

Reflection ist ein zentrales Feature von Java, damit Sie die Reflexion und die Vorteile der von GraalVM Native Image angebotenen Speedups nutzen können. Sie benötigen eine Möglichkeit, das Tool native-image über jede Verwendung von Reflexion zu informieren.

Zum Glück kann das Tool native-image in Konfigurationsdateien gelesen werden, die alle Klassen angeben, die durch Reflexion referenziert werden.

Sie können dies manuell tun, oder der Java Tracing Agent, der zur GraalVM Java Runtime gehört, kann dies für Sie tun. Der Agent generiert JSON-Dateien, die alle Reflexionsinstanzen, JNI-, Proxys- und Ressourcenzugriff aufzeichnen, die er bei der Ausführung der Anwendung finden kann.

Hinweis: Bei der Ausführung des Tracing Agents müssen alle Codepfade in Ihrer Anwendung ausgeführt werden, um sicherzustellen, dass alle Reflexionsfälle identifiziert werden.

Die vollständige Dokumentation für den Tracing Agent finden Sie hier.

Schritt 5: Tracing Agent verwenden

Generieren Sie nun mit dem Tracing Agent die Reflexionskonfiguration, während Sie die Anwendung ausführen.

  1. Führen Sie die Anwendung mit dem Tracing Agent aus:

    java -agentlib:native-image-agent=config-output-dir=./src/main/resources/META-INF/native-image -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    Sehen Sie sich die Konfigurationsdateien an, die der Tracing Agent erstellt hat:

    ls -l src/main/resources/META-INF/native-image/
    

    In der generierten Ausgabe sollte Folgendes enthalten sein:

    total 56
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 jni-config.json
    -rw-r--r--  1 kfoster  staff    86B Nov  9 20:46 native-image.properties
    -rw-r--r--  1 kfoster  staff    65B Dec  2 19:13 predefined-classes-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 proxy-config.json
    -rw-r--r--  1 kfoster  staff   521B Dec  2 19:13 reflect-config.json
    -rw-r--r--  1 kfoster  staff   101B Dec  2 19:13 resource-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 serialization-config.json
    

    Hinweis: Das Projekt enthält ein Maven-Profil, das dies für Sie tun kann. Führen Sie den folgenden Befehl aus, um den Tracing Agent zu verwenden:

    mvn clean package exec:exec -Pjava_agent
    
  2. Erstellen Sie nun die ausführbare Datei erneut. Diesmal werden die vom Tracing Agent erstellten Konfigurationsdateien angewendet:

    mvn package -Pnative
    
  3. Führen Sie schließlich die generierte Datei aus:

    time ./target/file-count
    

    Die ausführbare Datei funktioniert und erzeugt Logmeldungen wie erwartet in der Ausgabe.

Dies funktioniert, weil die vom Tracing Agent generierten Dateien die von Reflexion referenzierten Klassen aufgezeichnet haben. Das Tool native-image weiß jetzt, dass sie in der Anwendung verwendet werden und schließt sie daher nicht aus der generierten ausführbaren Datei aus.

Hinweis zur Position des -agentlib-Parameters

Beachten Sie, dass die Agent-Parameter müssen vor den Parametern -jar oder -classpath stehen. Sie müssen auch ein Verzeichnis angeben, in das die Dateien geschrieben werden sollen. Der empfohlene Speicherort befindet sich unter src/main/resources/META-INF/native-image. Dateien an diesem Speicherort werden automatisch vom Tool native-image abgerufen.

Hinweis zur Konfiguration der Generierung der ausführbaren Datei

Sie können Parameter auch mit einer Java-Eigenschaftendatei (standardmäßig src/main/resources/META-INF/native-image/native-image.properties) an das Tool native-image übergeben. Im Verzeichnis demo befindet sich eine Beispieldatei, die Ihnen eine Vorstellung davon vermittelt, welche Aktionen Sie damit ausführen können.

Schlussfolgerungen

In dieser Übung haben Sie verschiedene Funktionen von GraalVM Native Image kennengelernt:

  1. So generieren Sie eine Fast Executable-Datei aus einer Java-Befehlszeilenanwendung
  2. So erstellen Sie mit Maven eine ausführbare Datei
  3. So automatisieren Sie mit dem Tracing Agent den Prozess der Verfolgung und Aufzeichnung von Reflexionen

Schreiben Sie effiziente, sicherere und sofort skalierbare cloud-native Java-Anwendungen mit GraalVM Native Image.

Weitere Informationen

Weitere Lernressourcen

Sehen Sie sich andere Übungen zu docs.oracle.com/learn an, oder greifen Sie auf weitere Inhalte für kostenloses Lernen im Oracle Learning YouTube-Kanal zu. Außerdem besuchen Sie education.oracle.com/learning-explorer, um Oracle Learning Explorer zu werden.

Produktdokumentation finden Sie im Oracle Help Center.