Observação:

Conceitos Básicos do GraalVM Native Image

Introdução

Este laboratório orienta você passo a passo no processo de criação de um aplicativo Java nativo da nuvem com o GraalVM Native Image. Ele destina-se a desenvolvedores com conhecimento de Java.

A tecnologia GraalVM Native Image compila um código Java antecipadamente em um arquivo executável autocontido. Somente o código que é necessário no tempo de execução pelo aplicativo é adicionado ao arquivo executável.

Um arquivo executável produzido por Native Image tem várias vantagens importantes, pois ele:

Muitas das principais estruturas de microsserviços oferecem suporte à compilação antecipada com o GraalVM Native Image, incluindo Micronaut, Spring, Helidon e Quarkus.

Além disso, existem plug-ins Maven e Gradle para Native Image, para que você possa facilmente criar, testar e executar aplicativos Java como arquivos executáveis.

Observação: O Oracle Cloud Infrastructure (OCI) fornece o GraalVM Enterprise sem custo adicional.

Tempo estimado de laboratório: 45 minutos

Objetivos do laboratório

Neste laboratório, você executará as seguintes tarefas:

OBSERVAÇÃO: se você vir o ícone de laptop no laboratório, será necessário fazer algo como digitar um comando. Fique de olho nisso.

# This is where we you will need to do something

ETAPA 1: Estabelecer Conexão com um Host Remoto e Verificar o Ambiente de Desenvolvimento

Seu ambiente de desenvolvimento é fornecido por um host remoto: uma Instância de Computação do OCI com o Oracle Linux 8, 1 CPU e 32 GB de memória.
O ambiente de desktop do Luna Labs será exibido antes que o host remoto esteja pronto, o que pode levar até dois minutos.

A conexão com o host remoto é feita por meio da execução de um script de configuração no ambiente do Luna Desktop. Este script está disponível por meio da guia de recursos

  1. Na área de trabalho, clique duas vezes no ícone Luna-Lab.html. A página é aberta para exibir credenciais e informações do Oracle Cloud Infrastructure específicas do seu laboratório.

  2. A Guia Recursos será exibida. Observe que a engrenagem mostrada ao lado do título Recursos girará enquanto a instância de computação está sendo provisionada na nuvem teh.

  3. Quando a instância for provisionada, isso poderá levar até 2 minutos; você verá o seguinte exibido na guia Recursos

    Guia Recursos Luna

  4. Copie o script de configuração, que configura o ambiente do VS Code, na guia de recursos. Clique no link Exibir Detalhes para revelar a configuração. Copie isso conforme mostrado na captura de tela abaixo.

    Copiar Script de Configuração

  5. Abra um Terminal, conforme mostrado na captura de tela abaixo:

    Abrir Terminal

  6. Cole o código de configuração no terminal, que abrirá o VS Code para você.

    Colar Terminal 1

    Colar Terminal 2

E você acabou! Parabéns! Agora você foi conectado com sucesso a um host remoto no Oracle Cloud!

Observação sobre o Ambiente de Desenvolvimento

Você usará o GraalVM Enterprise 21, como a plataforma Java para este laboratório. O GraalVM é uma distribuição JDK de alto desempenho da Oracle baseada no Oracle Java SE confiável e seguro.

Seu ambiente de desenvolvimento vem pré-configurado com o GraalVM e as ferramentas Native Image necessárias para este laboratório.

Você pode verificar facilmente se executando esses comandos no terminal - pode criar um terminal no Código VS, Terminal > New Terminal:

java -version

native-image --version

ETAPA 2: Criar e Executar um Aplicativo de Demonstração

Usaremos um aplicativo de demonstração para mostrar o GraalVM Native Image: um aplicativo Java de linha de comando que conta o número de arquivos no diretório atual e seus subdiretórios. Como um extra agradável, o aplicativo também calcula o tamanho total dos arquivos.

O código-fonte do aplicativo está disponível no seu host remoto.

Observação sobre o Aplicativo Demo

O aplicativo consiste em dois arquivos Java que podem ser encontrados no diretório src:

O aplicativo pode ser criado manualmente ou com perfis Maven. A configuração de build do Maven é fornecida pelo arquivo pom.xml. Os Perfis Maven são uma ótima maneira de ter diferentes configurações de construção em um único arquivo pom.xml. Veja mais informações sobre os Perfis Maven aqui.

Você pode navegar pelos arquivos dentro do Código VS aberto.

Vários perfis serão usados neste laboratório, cada um dos quais tem um propósito específico:

  1. native : Este perfil cria um arquivo executável usando o GraalVM Native Image.
  2. java_agent : Este perfil cria o aplicativo Java com um agente de rastreamento que rastreia todos os usos do código dinâmico no seu aplicativo e captura essas informações em arquivos de configuração. Mais sobre isso mais tarde.

Você usa um perfil Maven específico especificando-o como parâmetro para o comando mvn. O nome do perfil é anexado ao flag -P. Você pode executar os comandos a seguir no Terminal do VS Code.

O exemplo abaixo mostra como você pode chamar um perfil native ao criar com o Maven:

mvn clean package -Pnative

Agora que você já tem uma compreensão básica do que o aplicativo executa para ver como ele funciona.

  1. Crie o projeto e execute-o dentro do Terminal que abrimos no VS Code:

    mvn clean package exec:exec
    

    O comando acima faz o seguinte:

    1. Limpa o projeto para remover qualquer artefato gerado ou compilado.
    2. Cria um arquivo JAR executável que contém o aplicativo. Este arquivo JAR será usado posteriormente pelo Native Image.
    3. Executa o aplicativo executando o plug-in exec.

    Você deverá ver o seguinte incluído na saída gerada (o número de arquivos reportados pelo aplicativo pode variar):

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

ETAPA 3: Transforme uma Aplicação Java em um Arquivo Executável

Em seguida, você criará uma versão executável do aplicativo usando o GraalVM Native Image. Como lembrete rápido, o GraalVM Native Image é uma tecnologia de compilação de data futura que converte seu aplicativo Java em um arquivo executável independente que não exige que um JDK seja executado, é rápido de iniciar e eficiente.

O GraalVM Native Image é pré-instalado no host remoto.

  1. Para começar, verifique se você tem um arquivo JAR compilado no diretório target:

    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
    

    O arquivo que você precisará é graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar.

  2. Gere um arquivo executável a partir da linha de comando. Você não precisa usar o plug-in Maven para usar o GraalVM Native Image, mas ele pode ajudar. Execute o seguinte no diretório raiz do projeto, demo:

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

    Isso gerará um arquivo executável chamado file-count no diretório atual.

  3. Execute este arquivo executável da seguinte forma:

    ./file-count
    
  4. Agora a hora do aplicativo. Primeiro, executando-o como um arquivo executável e, em seguida, usando o comando regular java:

    time ./file-count
    

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

    O arquivo executável, gerado pelo comando native-image, é executado significativamente mais rápido do que o aplicativo Java correspondente.

Vamos nos aprofundar um pouco mais em como você criou o arquivo executável.

Quais os parâmetros que você especificou para o comando native-image na etapa 2?

A documentação completa pode ser encontrada aqui.

Você também pode executar a ferramenta native-image usando o plug-in Maven do GraalVM Native Image. O arquivo pom.xml do projeto (configuração do Maven) contém o seguinte trecho de código que demonstra como criar um arquivo executável usando o plug-in:

<!-- 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>

O plug-in Maven de Native Image executa o trabalho pesado de criar o arquivo executável. Você pode desativá-lo usando a tag <skip>true</skip>. Observe também que você pode informar parâmetros para native-image por meio das tags <buildArgs/>.

A documentação completa sobre o plug-in GraalVM Native Image pode ser encontrada aqui.

Para criar o arquivo executável usando o perfil do Maven, execute:

mvn clean package -Pnative

O build do Maven coloca o arquivo executável, file-count, no diretório target.

Você pode executar o arquivo executável da seguinte forma:

./target/file-count

ETAPA 4: Usando Reflexão - Adicionando uma Dependência a Log4J

Nesta etapa, você criará um arquivo executável que funcione com os recursos dinâmicos do Java.

Digamos que você queira adicionar uma biblioteca, ou algum código, ao seu aplicativo que depende da reflexão. Um bom candidato para testar a reflexão é o framework de log Log4J. Ele já foi adicionado como uma dependência no arquivo pom.xml do projeto:

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

Para alterar o aplicativo de modo que ele use log4j, edite o arquivo ListDir.java e não comente algumas linhas.

  1. Abra o arquivo ListDir.java usando o VS Code

  2. Remova o comentário da linha que declara a importação log4j e, em seguida, cancele o comentário das seguintes linhas:

    //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. Salve o arquivo

    Agora que você adicionou o registro em log ao seu aplicativo, pode exibir o resultado de suas alterações recriando e executando.

    mvn clean package exec:exec
    

    Você deverá ver o mesmo tipo de saída que viu anteriormente, mas com a adição de mais logs.

  4. Em seguida, crie um arquivo executável usando o perfil Maven:

    mvn clean package -Pnative
    
  5. Execute o arquivo executável criado, que agora contém o log:

    ./target/file-count
    

    Isso gera um erro:

    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
    

    O que aconteceu aqui?

Trabalhando com os Recursos Dinâmicos do Java

Essa exceção é causada pela adição da biblioteca Log4J porque ela depende da reflexão.
A ferramenta native-image executa uma análise estática agressiva para ver quais classes são usadas no aplicativo. Para as classes não usadas, a ferramenta supõe que elas não são necessárias. Isso denomina-se pressuposto de "mundo fechado" - tudo o que precisa ser carregado deve ser conhecido ao construir um arquivo executável. Se não for localizável pela análise estática, ela não será incluída no arquivo executável.

A reflexão é um recurso básico do Java, de modo que você pode usar a reflexão e aproveitar as facilidades oferecidas pelo GraalVM Native Image? Você precisa de uma maneira de informar a ferramenta native-image sobre quaisquer usos de reflexão.

Felizmente, a ferramenta native-image é capaz de ler arquivos de configuração que especificam todas as classes que são referenciadas por meio da reflexão.

Você pode fazer isso manualmente ou o agente de rastreamento Java que vem com o runtime GraalVM Java pode fazer isso por você. O agente gera arquivos JSON que registram todas as instâncias de reflexão, JNI, proxies e acesso aos recursos que ele pode localizar enquanto seu aplicativo está em execução.

Observação : É importante exercer todos os caminhos de código em seu aplicativo ao executar o agente de rastreamento para garantir que todos os casos de reflexão sejam identificados.

A documentação completa do agente de rastreamento pode ser encontrada aqui.

ETAPA 5: Usando o Agente de Rastreamento

Agora use o agente de rastreamento para gerar a configuração de reflexão ao executar seu aplicativo.

  1. Execute o aplicativo com o agente de rastreamento:

    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
    

    Dê uma olhada nos arquivos de configuração que o agente de rastreamento criou:

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

    Você deverá ver o seguinte incluído na saída gerada:

    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
    

    Observação: o projeto contém um perfil Maven que pode fazer isso para você. Execute o seguinte comando para usar o agente de rastreamento:

    mvn clean package exec:exec -Pjava_agent
    
  2. Agora recrie o arquivo executável novamente. Desta vez, os arquivos de configuração produzidos pelo agente de rastreamento serão aplicados:

    mvn package -Pnative
    
  3. Por fim, execute o arquivo gerado:

    time ./target/file-count
    

    O arquivo executável funciona e produz mensagens de log para a saída, conforme esperado.

Isso funciona porque os arquivos gerados pelo agente de rastreamento registram as classes que são referenciadas pela reflexão. Agora a ferramenta native-image sabe que eles são usados dentro do aplicativo e, portanto, não os exclui do arquivo executável gerado.

Observação sobre a posição do parâmetro -agentlib

Observe que os parâmetros do agente devem vir antes de qualquer parâmetro -jar ou -classpath. Você também deve especificar um diretório no qual os arquivos serão gravados. O local recomendado está em src/main/resources/META-INF/native-image. Os arquivos colocados nesse local são selecionados automaticamente pela ferramenta native-image.

Observação sobre a Configuração da Geração do Arquivo Executável

Você também pode passar parâmetros para a ferramenta native-image usando um arquivo de propriedades Java (por padrão, src/main/resources/META-INF/native-image/native-image.properties). Há um arquivo de exemplo no diretório demo para dar a você uma ideia do que você pode fazer com ele.

Conclusões

Neste laboratório, você já experimentou vários recursos do GraalVM Native Image:

  1. Como gerar um arquivo executável rápido a partir de um aplicativo de linha de comando Java
  2. Como usar o Maven para criar um arquivo executável
  3. Como usar o agente de rastreamento para automatizar o processo de reflexão de rastreamento e gravação

Crie aplicativos Java nativos da nuvem eficientes, mais seguros e instantaneamente escaláveis com o GraalVM Native Image!

Saiba Mais

Mais Recursos de Aprendizagem

Explore outros laboratórios em docs.oracle.com/learn ou acesse mais conteúdo de aprendizado gratuito no canal YouTube do Oracle Learning. Além disso, visite education.oracle.com/learning-explorer para se tornar um Oracle Learning Explorer.

Para obter a documentação do produto, visite o Oracle Help Center.