Nota:

Descripción de Reflexión y GraalVM Native Image

Introducción

Este laboratorio está destinado a desarrolladores que deseen saber más sobre cómo funciona el reflejo en GraalVM Native Image.

GraalVM Native Image permite la compilación por adelantado de una aplicación Java en un ejecutable nativo independiente. Con GraalVM Native Image solo el código que necesita la aplicación en tiempo de ejecución se agrega al ejecutable nativo.

Estos ejecutables nativos tienen una serie de ventajas importantes, ya que:

Muchos de los marcos de microservicios líderes admiten la compilación anticipada con GraalVM Native Image, incluidos Micronaut, Spring, Helidon y Quarkus.

Además, hay plugins de Maven y Gradle para Native Image para facilitar la creación, la prueba y la ejecución de aplicaciones Java como ejecutables nativos.

Nota: Oracle Cloud Infrastructure (OCI) proporciona GraalVM Enterprise sin costo adicional.

Tiempo de laboratorio estimado: 30 minutos

Objetivos del laboratorio

En esta práctica, realizará las siguientes tareas:

NOTA: Siempre que vea el icono de portátil, debe hacer algo en algún lugar. Preste atención a estos.

# This is where we you will need to do something

Un host remoto proporciona su entorno de desarrollo: una instancia informática de OCI con Oracle Linux 8, 1 CPU y 32 GB de memoria.
El entorno de escritorio Luna Labs se mostrará antes de que el host remoto esté listo, lo que puede tardar hasta dos minutos.

PASO 1: conexión a un host virtual y comprobación del entorno de desarrollo

La conexión al host remoto se realiza mediante la ejecución de un script de configuración en el entorno de Luna Desktop. Este script está disponible en el separador Recursos

  1. En el escritorio, haga doble clic en el icono Luna-Lab.html. Se abre la página para mostrar las credenciales y la información de Oracle Cloud Infrastructure específicas de su laboratorio.

  2. Se mostrará el separador Recursos. Tenga en cuenta que el engranaje que se muestra junto al título Recursos girará mientras la instancia informática se aprovisiona en la nube de teh.

  3. Cuando se aprovisiona la instancia, esto puede tardar hasta 2 minutos, verá lo siguiente en el separador Recursos.

    Separador Recursos Luna

  4. Copie la secuencia de comandos de configuración, que configura el entorno de código VS, en la ficha Recursos. Haga clic en el enlace Ver detalles para mostrar la configuración. Copie esto como se muestra en la captura de pantalla siguiente.

    Copiar script de configuración

  5. Abra un terminal, como se muestra en la captura de pantalla siguiente:

    Abrir terminal

  6. Pegue el código de configuración en el terminal, que le abrirá VS Code.

    Pegar terminal 1

    Pegar terminal 2

¡Y ha terminado! Enhorabuena, ahora se ha conectado correctamente a un host remoto en Oracle Cloud.

PASO 2: la asunción mundial cerrada

La creación de un ejecutable independiente con la herramienta native-image que viene con GraalVM es un poco diferente a la creación de aplicaciones Java. Native Image hace uso de lo que se conoce como la suposición del mundo cerrado.

La suposición Mundo Cerrado significa que todo el código de byte de la aplicación que se puede llamar en tiempo de ejecución se debe conocer (observar y analizar) en tiempo de creación, es decir, cuando la herramienta native-image está creando el ejecutable autónomo.

Antes de continuar, conviene revisar el modelo de creación/ejecución para las aplicaciones creadas con GraalVM Native Image.

  1. Compile el código fuente Java en las clases de código de bytes Java
  2. Con la herramienta native-image, cree esas clases de código de bytes de Java en un ejecutable nativo
  3. Ejecutar el ejecutable nativo

Pero, ¿qué sucede realmente durante el paso 2?

En primer lugar, la herramienta native-image realiza un análisis para ver a qué clases de la aplicación se puede acceder. Lo examinaremos con más detalle en breve.

En segundo lugar, se inicializan las clases encontradas, que se sabe que son seguras para inicializarse (Automatic Initialization of Safe Classes). Los datos de clase de las clases inicializadas se cargan en la pila de imágenes que, a su vez, se guardan en ejecutable independiente (en la sección de texto). Esta es una de las funciones de la herramienta native-image de GraalVM que se puede realizar para aplicaciones de inicio rápido.

NOTA: No es lo mismo que la inicialización de objetos. La inicialización de objetos se produce durante el tiempo de ejecución del ejecutable nativo.

Dijimos que volveríamos al tema de la accesibilidad. Como se mencionó anteriormente, el análisis determina qué clases, métodos y campos deben incluirse en el ejecutable independiente. El análisis es estático, es decir, no ejecuta el código. El análisis puede determinar algunos casos de carga de clases dinámicas y usos de reflexión (véase ), pero hay casos en los que no podrá recoger.

Para tratar las características dinámicas de Java, se debe informar al análisis sobre qué clases utilizan la reflexión o qué clases se cargan dinámicamente.

Veamos un ejemplo.

PASO 3: ejemplo con reflejo

Imagine que tiene la siguiente clase, ReflectionExample.java (se puede encontrar una copia de esta en el directorio, 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);
    }
}

En primer lugar, cree un terminal dentro del código VS. Esto se realiza desde Terminal > New Terminal.

A continuación, vamos a crear el código. En el shell, desde dentro del código de VS, ejecute el siguiente comando:

javac ReflectionExample.java

El método main de la clase ReflectionExample carga una clase cuyo nombre se ha transferido como argumento, un caso de uso muy dinámico. El segundo argumento para la clase es el nombre del método en la clase cargada dinámicamente que se debe llamar.

Vamos a ejecutarlo y ver lo que hace.

java ReflectionExample StringReverser reverse "hello"

Como esperábamos, se encontró el método reverse en la clase StringReverser, a través de la reflexión. Se invocó el método y revirtió nuestra entrada Cadena de "hello". Hasta ahora, tan bueno.

OK, pero ¿qué sucede si intentamos construir una imagen nativa fuera del programa? Vamos a intentarlo. En el shell, ejecute el siguiente comando:

native-image --no-fallback ReflectionExample

NOTA: la opción --no-fallback de native-image hace que falle la creación si no puede crear una configuración regional nativa independiente.

Ahora vamos a ejecutar el ejecutable nativo generado y ver lo que hace:

./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)

¿Qué sucede aquí? Parece que nuestro ejecutable nativo no ha podido encontrar la clase, StringReverser. ¿Cómo sucedió esto? Por ahora, creo que probablemente tenemos una idea de por qué. Asunción del Mundo Cerrado.

Durante el análisis que la herramienta native-image ha realizado, no ha podido determinar que se ha utilizado la clase StringReverser. Por lo tanto, eliminó la clase del ejecutable nativo que generó. Nota: Al eliminar clases no deseadas del ejecutable independiente, la herramienta para reducir el código que se crea solo incluyendo las clases que se sabe que se utilizan. Como acabamos de ver, esto puede dar lugar a problemas con la reflexión, pero por suerte hay una manera de abordar esto.

PASO 4: Introducción a la configuración de reflexión de la imagen nativa

Podemos indicarle a la herramienta de creación native-image las instancias de reflexión a través de archivos de configuración especiales. Estos archivos se escriben en JSON y se pueden transferir a la herramienta native-image mediante el uso de indicadores.

Por lo tanto, ¿qué otros tipos de información de configuración podemos transferir a la herramienta de creación native-image? Las herramientas soportan actualmente la lectura de archivos que contienen detalles sobre:

Solo estamos mirando cómo tratar la reflexión en este laboratorio, por lo que nos centraremos en eso.

A continuación, se muestra un ejemplo del aspecto de estos archivos (tomado de aquí):

[
  {
    "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" }
    ]
  }
]

A partir de esto, podemos ver que se deben configurar las clases y los métodos a los que se accede a través de la API de reflejo. Podemos hacerlo manualmente, pero la forma más cómoda de generar estos archivos de configuración es mediante el uso de la configuración asistida javaagent.

PASO 5: Native Image, configuración asistida: introducción al agente Java

Escribir un archivo de configuración de reflejo completo desde cero es, sin duda, posible, pero el tiempo de ejecución de GraalVM Java proporciona un agente de rastreo java, javaagent, que lo generará automáticamente al ejecutar la aplicación.

Vamos a intentarlo.

Ejecute la aplicación con el agente de rastreo activado. En nuestro shell, ejecute lo siguiente:

# 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"

Rastreo de configuración de agente

Veamos la configuración creada:

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

Puede ejecutar este proceso con varios tiempos y las ejecuciones se fusionan si especifica native-image-agent=config-merge-dir, como se muestra en el siguiente ejemplo:

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

La creación del ejecutable independiente utilizará ahora la configuración proporcionada. Vamos a crearlo:

native-image --no-fallback ReflectionExample

Y veamos si funciona mejor:

./reflectionexample StringReverser reverse "hello"

¡Se realiza!

Conclusiones

La creación de ejecutables independientes con GraalVM Native Image se basa en la suposición del mundo cerrado, que es necesario saber con antelación, a la hora de crear ejecutables independientes, sobre cualquier caso de reflexión que pueda ocurrir en nuestro código.

La plataforma GraalVM proporciona una forma de especificar a la herramienta de creación native-image cuando se utiliza la reflexión. Nota: Para algunos casos simples, la herramienta native-image puede detectarlos por sí misma.

La plataforma GraalVM también proporciona una forma de detectar los usos de la reflexión y otros comportamientos dinámicos a través del agente de rastreo de Java y puede generar automáticamente los archivos de configuración que necesita la herramienta native-image.

Hay algunas cosas que debe tener en cuenta al utilizar el agente de rastreo:

Esperamos que haya disfrutado de este tutorial y que haya aprendido algo sobre cómo podemos abordar la reflexión al utilizar Native Image.

Más información

Más recursos de aprendizaje

Explore otras prácticas en docs.oracle.com/learn o acceda a contenido de aprendizaje más gratuito en el canal YouTube de Oracle Learning. Además, visite education.oracle.com/learning-explorer para convertirse en un explorador de formación de Oracle.

Para obtener documentación sobre los productos, visite Oracle Help Center.