Nota:

Informazioni sulla riflessione e sull'immagine nativa GraalVM

Introduzione

Questo laboratorio è dedicato agli sviluppatori che desiderano saperne di più sul funzionamento delle riflessioni nell'immagine nativa di GraalVM.

GraalVM Native Image consente la compilazione precedente di un'applicazione Java in un eseguibile auto-contenuto nativo. Con l'immagine nativa GraalVM, solo il codice richiesto dall'applicazione in fase di esecuzione viene aggiunto all'eseguibile nativo.

Questi eseguibili nativi hanno una serie di vantaggi importanti, in quanto:

Molti dei principali framework di microservizi supportano la compilazione in anticipo con l'immagine nativa GraalVM, tra cui Micronaut, Spring, Helidon e Quarkus.

Inoltre, esistono plugin Maven e Gradle per l'immagine nativa per semplificare la creazione, il test e l'esecuzione di applicazioni Java come eseguibili nativi.

Nota: Oracle Cloud Infrastructure (OCI) offre GraalVM Enterprise senza costi aggiuntivi.

Tempo di laboratorio stimato: 30 minuti

Obiettivi laboratorio

In questo laboratorio verranno eseguiti i seguenti task:

NOTA: ogni volta che viene visualizzata l'icona del laptop, è necessario eseguire le operazioni desiderate. Guardate questi.

# This is where we you will need to do something

L'ambiente di sviluppo viene fornito da un host remoto: un'istanza di computazione OCI con Oracle Linux 8, 1 CPU e 32 GB di memoria.
L'ambiente desktop Luna Labs verrà visualizzato prima che l'host remoto sia pronto, che può richiedere fino a due minuti.

STEP 1: connessione a un host virtuale e controllo dell'ambiente di sviluppo

La connessione all'host remoto viene eseguita mediante l'esecuzione di uno script di installazione nell'ambiente desktop Luna. Questo script è disponibile tramite la scheda Risorse

  1. Nel desktop fare doppio clic sull'icona Luna-Lab.html. Viene visualizzata la pagina per visualizzare le credenziali e le informazioni di Oracle Cloud Infrastructure specifiche del laboratorio.

  2. Verrà visualizzata la scheda Risorse. Tenere presente che la struttura visualizzata accanto al titolo Risorse verrà attivata mentre viene eseguito il provisioning dell'istanza di computazione nel cloud teh.

  3. Quando viene eseguito il provisioning dell'istanza, l'operazione potrebbe richiedere fino a 2 minuti, nella scheda Risorse verrà visualizzato quanto segue.

    Luna: scheda Risorse

  4. Copiare lo script di configurazione, che imposta l'ambiente Codice VS dalla scheda Risorse. Fare clic sul collegamento Visualizza dettagli per visualizzare la configurazione. Copiare questo file come mostrato nella schermata qui sotto.

    Copia script di configurazione

  5. Aprire un terminale, come mostrato nello screenshot seguente:

    Apri terminale

  6. Incollare il codice di configurazione nel terminale, che aprirà il codice VS.

    Incolla terminale 1

    Incolla terminale 2

E tu sei fatto! Congratulazioni. La connessione a un host remoto in Oracle Cloud è stata completata.

STEP 2: il presupposto mondiale chiuso

Creare un eseguibile standalone con lo strumento native-image fornito con GraalVM è un po' diverso dalla creazione di applicazioni Java. L'immagine nativa utilizza ciò che è noto come presupposto del mondo chiuso.

L'ipotesi del mondo chiuso indica che tutto il codice bytecode nell'applicazione che può essere chiamato in fase di esecuzione deve essere noto (osservato e analizzato) in fase di creazione, ovvero quando lo strumento native-image sta creando l'eseguibile standalone.

Prima di continuare, vale la pena seguire il modello di creazione/esecuzione per le applicazioni create con l'immagine nativa GraalVM.

  1. Compila il tuo codice sorgente Java in classi di codici byte Java
  2. Utilizzando lo strumento native-image, creare tali classi di codici byte Java in un eseguibile nativo
  3. Esegui eseguibile nativo

Ma, cosa accade davvero al passo 2?

In primo luogo, lo strumento native-image esegue un'analisi per vedere quali classi all'interno dell'applicazione sono raggiungibili. A breve ne analizzeremo più in dettaglio.

In secondo luogo, vengono inizializzate le classi notoriamente da inizializzare in sicurezza (inizializzazione automatica delle classi sicure). I dati di classe delle classi inizializzate vengono caricati nell'heap dell'immagine, che a sua volta viene salvato nell'eseguibile standalone (nella sezione di testo). Questa è una delle caratteristiche dello strumento GraalVM native-image che può creare applicazioni di avvio così rapido.

NOTA: non corrisponde all'inizializzazione dell'oggetto. L'inizializzazione dell'oggetto si verifica durante il runtime dell'eseguibile nativo.

Abbiamo detto che torneremo al tema della portata. Come indicato in precedenza, l'analisi determina quali classi, metodi e campi devono essere inclusi nell'eseguibile standalone. L'analisi è statica, ovvero non esegue il codice. L'analisi può determinare alcuni casi di caricamento e utilizzo dinamico delle classi (vedere ), ma ci sono casi che non sarà in grado di riprendere.

Per gestire le funzioni dinamiche di Java, è necessario fornire informazioni sull'utilizzo della riflessione delle classi o sulle classi caricate in modo dinamico.

Di seguito è riportato un esempio.

STEP 3: esempio utilizzando la selezione

Si supponga di disporre della seguente classe, ReflectionExample.java (una copia di questa è disponibile nella directory, demo/ReflectionExample.java):

import java.lang.reflect.Method;

class StringReverser {
    static String reverse(String input) {
        return new StringBuilder(input).reverse().toString();
    }
}

class StringCapitalizer {
    static String capitalize(String input) {
        return input.toUpperCase();
    }
}

public class ReflectionExample {
    public static void main(String[] args) throws ReflectiveOperationException {
        String className = args[0];
        String methodName = args[1];
        String input = args[2];

        Class<?> clazz = Class.forName(className);
        Method method = clazz.getDeclaredMethod(methodName, String.class);
        Object result = method.invoke(null, input);
        System.out.println(result);
    }
}

In primo luogo, creare un terminale all'interno del codice VS. Questa operazione viene eseguita da Terminal > New Terminal

Successivamente, creiamo il codice. Nella shell, dall'interno del codice VS, eseguire il seguente comando:

javac ReflectionExample.java

Il metodo principale nella classe ReflectionExample carica una classe il cui nome è stato passato come argomento, un caso d'uso molto dinamico! Il secondo argomento della classe è il nome del metodo della classe caricata dinamicamente da richiamare.

Facciamola e vediamo cosa fa.

java ReflectionExample StringReverser reverse "hello"

Come ci aspettavamo, il metodo reverse sulla classe StringReverser è stato trovato, attraverso riflessione. Il metodo è stato invocato e ha invertito il nostro input String di "hello". Fino ad ora, così bene.

OK, ma cosa succede se cerchiamo di creare un'immagine nativa fuori dal programma? Proviamo. Nella shell eseguire il comando seguente:

native-image --no-fallback ReflectionExample

NOTA: l'opzione --no-fallback su native-image causa l'errore della build se non è in grado di creare un'eseguutabale nativo standalone.

Ora eseguiamo l'eseguibile nativo generato e vediamo cosa fa:

./reflectionexample StringReverser reverse "hello"

Exception in thread "main" java.lang.ClassNotFoundException: StringReverser
	at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
	at java.lang.Class.forName(DynamicHub.java:1214)
	at ReflectionExample.main(ReflectionExample.java:21)

Che cosa è successo qui? Sembra che il nostro eseguibile nativo non sia stato in grado di trovare la classe, StringReverser. Come è successo? A questo punto, probabilmente abbiamo un ’ idea del perché. Il presupposto del mondo chiuso.

Durante l'analisi eseguita dallo strumento native-image, non è stato possibile determinare l'utilizzo della classe StringReverser. Pertanto ha rimosso la classe dall'eseguibile nativo generato. Nota: rimuovendo le classi indesiderate dall'eseguibile standalone, lo strumento per ridurre il codice creato includendo solo le classi notoriamente utilizzate. Come abbiamo appena visto, la riflessione può riguardare temi, ma fortunatamente c ’ è un modo per affrontarli.

STEP 4: introduzione della configurazione di riflessione delle immagini native

Possiamo fornire allo strumento di creazione native-image informazioni sulle istanze di riflessione mediante file di configurazione speciali. Questi file vengono scritti in JSON e possono essere passati allo strumento native-image mediante l'uso di flag.

Quindi, quali altri tipi di informazioni di configurazione possiamo passare allo strumento di creazione native-image? Lo strumento attualmente supporta file di lettura contenenti dettagli su:

Stiamo solo esaminando le modalità di riflessione in questo laboratorio, per cui ci concentreremo su questo aspetto.

Di seguito è riportato un esempio dell'aspetto di questi file (tratto da qui):

[
  {
    "name" : "java.lang.Class",
    "queryAllDeclaredConstructors" : true,
    "queryAllPublicConstructors" : true,
    "queryAllDeclaredMethods" : true,
    "queryAllPublicMethods" : true,
    "allDeclaredClasses" : true,
    "allPublicClasses" : true
  },
  {
    "name" : "java.lang.String",
    "fields" : [
      { "name" : "value" },
      { "name" : "hash" }
    ],
    "methods" : [
      { "name" : "<init>", "parameterTypes" : [] },
      { "name" : "<init>", "parameterTypes" : ["char[]"] },
      { "name" : "charAt" },
      { "name" : "format", "parameterTypes" : ["java.lang.String", "java.lang.Object[]"] }
    ]
  },
  {
    "name" : "java.lang.String$CaseInsensitiveComparator",
    "queriedMethods" : [
      { "name" : "compare" }
    ]
  }
]

Da questo è possibile osservare che le classi e i metodi a cui si accede tramite l'API di riflessione devono essere configurati. È possibile eseguire questa operazione manualmente, ma il modo più conveniente per generare questi file di configurazione è l'uso della configurazione assistita javaagent.

STEP 5: immagine nativa, configurazione assistita: immettere l'agente Java

La scrittura di un file di configurazione di riflessione completo da zero è certamente possibile, ma il runtime Java di GraalVM fornisce un agente di trace java, il file javaagent, che ne genererà automaticamente quando si esegue l'applicazione.

proviamo questo.

Eseguire l'applicazione con l'agente di trace abilitato. Nella nostra shell eseguire:

# Note: the tracing agent parameter must come before the classpath and jar params on the command ine
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"

Configurazione agente di trace

Di seguito viene riportata la configurazione creata.

cat META-INF/native-image/reflect-config.json
[
    {
    "name":"StringReverser",
    "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
    }
]

È possibile eseguire questo processo per due volte e le esecuzioni vengono unite se si specifica native-image-agent=config-merge-dir, come mostrato nell'esempio riportato di seguito.

java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"

La creazione dell'eseguibile standalone ora utilizzerà la configurazione fornita. Costruiamolo:

native-image --no-fallback ReflectionExample

E vediamo se funziona meglio:

./reflectionexample StringReverser reverse "hello"

È così!

Conclusioni

La creazione di eseguibili standalone con l'immagine nativa GraalVM si basa sull'ipotesi Chiuso nel mondo, che è necessario conoscere in anticipo, quando si creano eseguibili standalone, su qualsiasi caso di riflessione che può verificarsi nel nostro codice.

La piattaforma GraalVM consente di specificare lo strumento di creazione native-image quando viene utilizzata la riflessione. Nota: per alcuni casi semplici, lo strumento native-image può trovarli per se stesso.

La piattaforma GraalVM offre anche un modo per scoprire gli utilizzi di riflessioni e altri comportamenti dinamici tramite l'agente di trace Java e può generare automaticamente i file di configurazione necessari dallo strumento native-image.

Ci sono alcune cose che si dovrebbe tenere presente quando si utilizza l'agente di rintracciamento:

Ci auguriamo che l'esercitazione sia stata apprezzata e che l'utente abbia imparato qualcosa su come gestire la riflessione quando utilizza l'immagine nativa.

Per saperne di più

Altre risorse di apprendimento

Esplora altri laboratori su docs.oracle.com/learn o accedi a più contenuti di apprendimento gratuito sul canale Oracle Learning YouTube. Inoltre, visitare education.oracle.com/learning-explorer per diventare Oracle Learning Explorer.

Per la documentazione del prodotto, visitare il sito Oracle Help Center.