Guia do desenvolvedor de Java do Azure Functions

Este guia contém informações detalhadas para ajudar você a desenvolver com sucesso o Azure Functions usando o Java.

Como desenvolvedor de Java, se você for novo no Azure Functions, considere ler um dos seguintes artigos primeiro:

Introdução Conceitos Cenários/exemplos

Noções básicas da função de linguagem Java

Uma função Java é um método public, decorado com a anotação @FunctionName. Esse método define a entrada para uma função Java e deve ser exclusivo em um pacote específico. O pacote pode ter várias classes com vários métodos públicos anotados com @FunctionName. Um único pacote é implantado em um aplicativo de funções no Azure. No Azure, o aplicativo de funções fornece a implantação, execução e contexto de gerenciamento para suas funções Java individuais.

Modelo de programação

Os conceitos de gatilhos e ligações são fundamentais para as Funções do Azure. Os gatilhos iniciam a execução do seu código. As ligações fornecem uma maneira de transmitir dados e retornar dados de uma função, sem precisar escrever código de acesso a dados personalizado.

Criar funções Java

Para facilitar a criação de funções Java, há ferramentas baseadas no Maven e arquétipos que usam modelos Java predefinidos para ajudá-lo a criar projetos com um gatilho de função específico.

Ferramentas baseadas no Maven

Os seguintes ambientes de desenvolvedor têm ferramentas do Azure Functions que permitem criar projetos de funções Java:

Esses artigos mostram como criar suas primeiras funções usando o IDE de sua escolha.

Scaffolding do projeto

Caso você prefira o desenvolvimento de linha de comando do Terminal, a maneira mais simples de fazer o scaffolding de projetos de funções baseados em Java é com arquétipos Apache Maven. O arquétipo Java Maven para o Azure Functions é publicado na seguinte groupId:artifactId: com.microsoft.azure:azure-functions-archetype.

O seguinte comando gera um novo projeto de função Java usando esse arquétipo:

mvn archetype:generate \
    -DarchetypeGroupId=com.microsoft.azure \
    -DarchetypeArtifactId=azure-functions-archetype

Para começar a usar esse arquétipo, confira o Início rápido do Java.

Estrutura de pastas

Aqui está a estrutura de pastas de um projeto Java do Azure Functions:

FunctionsProject
 | - src
 | | - main
 | | | - java
 | | | | - FunctionApp
 | | | | | - MyFirstFunction.java
 | | | | | - MySecondFunction.java
 | - target
 | | - azure-functions
 | | | - FunctionApp
 | | | | - FunctionApp.jar
 | | | | - host.json
 | | | | - MyFirstFunction
 | | | | | - function.json
 | | | | - MySecondFunction
 | | | | | - function.json
 | | | | - bin
 | | | | - lib
 | - pom.xml

Você pode usar um arquivo host.json compartilhado para configurar o aplicativo de funções. Cada função possui seu próprio arquivo de código (.java) e arquivo de configuração de associação (function.json).

Você pode colocar mais de uma função em um projeto. Evite colocar suas funções no jars separados. O FunctionApp no diretório de destino é o que é implantado em seu aplicativo de funções no Azure.

Gatilhos e anotações

As funções são invocadas por um gatilho, como uma solicitação HTTP, um temporizador ou uma atualização para os dados. Sua função precisa processar esse gatilho e quaisquer outras entradas para produzir uma ou mais saídas.

Use as anotações Java incluídas no pacote com.microsoft.azure.functions.annotation. * para ligar entradas e saídas a seus métodos. Para obter mais informações, confira os documentos de referência Java.

Importante

Você precisa configurar uma conta de Armazenamento do Azure em local.settings.json para executar localmente os gatilhos de armazenamento de Blob do Azure, armazenamento de Filas do Azure ou Armazenamento de Tabelas do Azure.

Exemplo:

public class Function {
    public String echo(@HttpTrigger(name = "req", 
      methods = {HttpMethod.POST},  authLevel = AuthorizationLevel.ANONYMOUS) 
        String req, ExecutionContext context) {
        return String.format(req);
    }
}

Aqui está o function.json correspondente gerado pelo azure-functions-maven-plugin:

{
  "scriptFile": "azure-functions-example.jar",
  "entryPoint": "com.example.Function.echo",
  "bindings": [
    {
      "type": "httpTrigger",
      "name": "req",
      "direction": "in",
      "authLevel": "anonymous",
      "methods": [ "GET","POST" ]
    },
    {
      "type": "http",
      "name": "$return",
      "direction": "out"
    }
  ]
}

Versões do Java

A versão do Java em que seu aplicativo é executado no Azure é especificada no arquivo pom.xml. O arquétipo Maven atualmente gera um pom.xml para Java 8, que você pode alterar antes da publicação. A versão Java no pom.xml deve corresponder à versão na qual você desenvolveu e testou localmente seu aplicativo.

Versões com suporte

A seguinte tabela mostra as versões Java com suporte atuais para cada versão principal do runtime do Functions, por sistema operacional:

Versão do Functions Versões Java (Windows) Versões Java (Linux)
4.x 17
11
8
21 (Versão prévia)
17
11
8
3.x 11
8
11
8
2. x 8 N/D

A menos que você especifique uma versão Java para a sua implantação, o Maven define o Java 8 como padrão durante a implantação no Azure.

Especifique a versão de implantação

Você pode controlar a versão Java direcionada pelo arquétipo Maven usando o parâmetro -DjavaVersion. O valor desse parâmetro pode ser 8, 11, 17 ou 21.

O arquétipo Maven gera um pom.xml que tem como destino a versão Java especificada. Os seguintes elementos no pom.xml indicam a versão do Java a ser usada:

Elemento Valor do Java 8 Valor do Java 11 Valor do Java 17 Valor do Java 21 (versão prévia, Linux) Descrição
Java.version 1.8 11 17 21 Versão do Java usada pelo maven-compiler-plugin.
JavaVersion 8 11 17 21 Versão do Java hospedada pelo aplicativo de funções no Azure.

Os exemplos a seguir mostram as configurações para Java 8 nas seções relevantes do arquivo pom.xml:

Java.version

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <azure.functions.maven.plugin.version>1.6.0</azure.functions.maven.plugin.version>
    <azure.functions.java.library.version>1.3.1</azure.functions.java.library.version>
    <functionAppName>fabrikam-functions-20200718015742191</functionAppName>
    <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
</properties>

JavaVersion

<runtime>
    <!-- runtime os, could be windows, linux or docker-->
    <os>windows</os>
    <javaVersion>8</javaVersion>
    <!-- for docker function, please set the following parameters -->
    <!-- <image>[hub-user/]repo-name[:tag]</image> -->
    <!-- <serverId></serverId> -->
    <!-- <registryUrl></registryUrl>  -->
</runtime>

Importante

Você deve ter a variável de ambiente JAVA_HOME definida corretamente para o diretório do JDK que é usado durante a compilação de código usando o Maven. Certifique-se de que a versão do JDK seja pelo menos tão recente quanto a configuração Java.version.

Especifique o SO de implantação

O Maven também permite especificar o sistema operacional no qual seu aplicativo de funções é executado no Azure. Use o elemento os para escolher o sistema operacional.

Elemento Windows Linux Docker
os windows linux docker

O exemplo a seguir mostra a configuração do sistema operacional na seção runtime do arquivo pom.xml:

<runtime>
    <!-- runtime os, could be windows, linux or docker-->
    <os>windows</os>
    <javaVersion>8</javaVersion>
    <!-- for docker function, please set the following parameters -->
    <!-- <image>[hub-user/]repo-name[:tag]</image> -->
    <!-- <serverId></serverId> -->
    <!-- <registryUrl></registryUrl>  -->
</runtime>

Suporte e disponibilidade de runtime do JDK

Os builds da Microsoft e da Adoptium do OpenJDK são fornecidos e têm suporte no Functions para Java 8 (Adoptium), 11, 17 (MSFT) e 21 (MSFT). Esses binários são fornecidos como uma distribuição pronta para produção, sem custo e multiplataforma do OpenJDK para o Azure. Eles contêm todos os componentes para criar e executar aplicativos Java SE.

Para desenvolvimento ou teste local, você pode baixar os binários de Build da Microsoft de OpenJDK ou Adoptium Temurin gratuitamente. O Suporte do Azure está disponível para solucionar problemas com os JDKs e os aplicativos de funções com um plano de suporte qualificado.

Se você quiser continuar usando o Zulu para binários do Azure em seu aplicativo de funções, configure seu aplicativo de acordo. Você pode continuar usando os binários Azul para seu site. No entanto, os patches ou melhorias de segurança só estão disponíveis em novas versões do OpenJDK. Por isso, você deve remover essa configuração para que seus aplicativos usem a versão mais recente disponível do Java.

Personalizar JVM

O Functions permite que você personalize a JVM (máquina virtual Java) usada para executar suas funções Java. As seguintes opções de JVM são usadas por padrão:

  • -XX:+TieredCompilation
  • -XX:TieredStopAtLevel=1
  • -noverify
  • -Djava.net.preferIPv4Stack=true
  • -jar

Você pode fornecer outros argumentos para a JVM usando uma das seguintes configurações de aplicativo, dependendo do tipo de plano:

Tipo de plano Nome da configuração Comentário
Plano de Consumo languageWorkers__java__arguments Essa configuração aumenta os tempos de inicialização a frio para funções Java em execução em um plano de consumo.
Plano Premium
Plano dedicado
JAVA_OPTS

As seções a seguir mostram como adicionar essas configurações. Para saber mais sobre como trabalhar com as configurações do aplicativo, consulte a seção Trabalhar com as configurações do aplicativo.

Portal do Azure

No portal do Azure, use a guia Configurações de Aplicativo para adicionar a configuração languageWorkers__java__arguments ou JAVA_OPTS.

CLI do Azure

Você pode usar o comando az functionapp config appsettings set para adicionar essas configurações, conforme mostrado no seguinte exemplo para a opção -Djava.awt.headless=true:

az functionapp config appsettings set \
    --settings "languageWorkers__java__arguments=-Djava.awt.headless=true" \
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>

Este exemplo habilita o modo sem periféricos. Substitua <APP_NAME> pelo nome do seu aplicativo de funções e <RESOURCE_GROUP> pelo grupo de recursos.

Bibliotecas de terceiros

O Azure Functions oferece suporte ao uso de bibliotecas de terceiros. Por padrão, todas as dependências especificadas no arquivo pom.xml do projeto são agrupadas automaticamente durante a meta mvn package. Para bibliotecas não especificadas como dependências no pom.xmlarquivo, coloque-as em um diretóriolib no diretório-raiz da função. As dependências colocadas no diretório lib são adicionadas ao carregador de classe do sistema em runtime.

A dependência com.microsoft.azure.functions:azure-functions-java-library é fornecida no classpath por padrão e não precisa ser incluída no diretório lib. Além disso,o azure-functions-java-worker adiciona as dependências listadas aqui ao classpath.

Suporte do tipo de dados

Você pode usar POJOs (objetos Java básicos), tipos definidos em azure-functions-java-library ou tipos de dados primitivos, como String e Integer, para vincular a associações de entrada ou saída.

POJOs

Para converter dados de entrada em POJO, o azure-functions-java-worker usa a biblioteca gson. Tipos de POJO usados como entradas para funções devem ser public.

Dados binários

Associe entradas ou saídas binárias a byte[], definindo o campo dataType em function.json como binary:

   @FunctionName("BlobTrigger")
    @StorageAccount("AzureWebJobsStorage")
     public void blobTrigger(
        @BlobTrigger(name = "content", path = "myblob/{fileName}", dataType = "binary") byte[] content,
        @BindingName("fileName") String fileName,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Blob trigger function processed a blob.\n Name: " + fileName + "\n Size: " + content.length + " Bytes");
    }

Se você espera valores nulos, use Optional<T>.

Associações

As ligações de entrada e saída fornecem uma maneira declarativa de se conectar aos dados de dentro do seu código. Uma função pode ter várias ligações de entrada e saída.

Exemplo de associação de entrada

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("echo")
    public static String echo(
        @HttpTrigger(name = "req", methods = { HttpMethod.PUT }, authLevel = AuthorizationLevel.ANONYMOUS, route = "items/{id}") String inputReq,
        @TableInput(name = "item", tableName = "items", partitionKey = "Example", rowKey = "{id}", connection = "AzureWebJobsStorage") TestInputData inputData,
        @TableOutput(name = "myOutputTable", tableName = "Person", connection = "AzureWebJobsStorage") OutputBinding<Person> testOutputData
    ) {
        testOutputData.setValue(new Person(httpbody + "Partition", httpbody + "Row", httpbody + "Name"));
        return "Hello, " + inputReq + " and " + inputData.getKey() + ".";
    }

    public static class TestInputData {
        public String getKey() { return this.rowKey; }
        private String rowKey;
    }
    public static class Person {
        public String partitionKey;
        public String rowKey;
        public String name;

        public Person(String p, String r, String n) {
            this.partitionKey = p;
            this.rowKey = r;
            this.name = n;
        }
    }
}

Você invoca essa função com uma solicitação HTTP.

  • A carga de solicitação HTTP é passada como um String para o argumento inputReq.
  • Uma entrada é recuperada do armazenamento de tabela e é passada como TestInputData para o argumento inputData.

Para receber um lote de entradas, você pode associar a String[], POJO[], List<String> ou List<POJO>.

@FunctionName("ProcessIotMessages")
    public void processIotMessages(
        @EventHubTrigger(name = "message", eventHubName = "%AzureWebJobsEventHubPath%", connection = "AzureWebJobsEventHubSender", cardinality = Cardinality.MANY) List<TestEventData> messages,
        final ExecutionContext context)
    {
        context.getLogger().info("Java Event Hub trigger received messages. Batch size: " + messages.size());
    }
    
    public class TestEventData {
    public String id;
}

Essa função é acionada sempre que houver novos dados no hub de eventos configurado. Como q cardinality é definidq para MANY, a função recebe um lote de mensagens do hub de eventos. EventData do hub de eventos é convertido em TestEventData para a execução da função.

Exemplo de associação de saída

Você pode vincular uma associação de saída ao valor retornado usando $return.

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("copy")
    @StorageAccount("AzureWebJobsStorage")
    @BlobOutput(name = "$return", path = "samples-output-java/{name}")
    public static String copy(@BlobTrigger(name = "blob", path = "samples-input-java/{name}") String content) {
        return content;
    }
}

Se houver várias associações de saída, use o valor de retorno de apenas um deles.

Para enviar vários valores de saída, use OutputBinding<T> definido no pacote azure-functions-java-library.

@FunctionName("QueueOutputPOJOList")
    public HttpResponseMessage QueueOutputPOJOList(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
            HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            @QueueOutput(name = "itemsOut", queueName = "test-output-java-pojo", connection = "AzureWebJobsStorage") OutputBinding<List<TestData>> itemsOut, 
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");
       
        String query = request.getQueryParameters().get("queueMessageId");
        String queueMessageId = request.getBody().orElse(query);
        itemsOut.setValue(new ArrayList<TestData>());
        if (queueMessageId != null) {
            TestData testData1 = new TestData();
            testData1.id = "msg1"+queueMessageId;
            TestData testData2 = new TestData();
            testData2.id = "msg2"+queueMessageId;

            itemsOut.getValue().add(testData1);
            itemsOut.getValue().add(testData2);

            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + queueMessageId).build();
        } else {
            return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Did not find expected items in CosmosDB input list").build();
        }
    }

     public static class TestData {
        public String id;
    }

Você invoca essa função em um objeto HttpRequest. Ela grava vários valores no armazenamento de Filas.

HttpRequestMessage e HttpResponseMessage

Eles são definidos em azure-functions-java-library. Eles são tipos auxiliares para trabalhar com as funções HttpTrigger.

Tipo especializado Destino Uso típico
HttpRequestMessage<T> Gatilho de HTTP Obtém método, cabeçalhos ou consultas
HttpResponseMessage Associação de saída HTTP Retorna um status diferente de 200

Metadados

Poucos gatilhos enviam gatilhos de metadados juntamente com dados de entrada. Você pode usar a anotação @BindingName para associar aos metadados do gatilho.

package com.example;

import java.util.Optional;
import com.microsoft.azure.functions.annotation.*;


public class Function {
    @FunctionName("metadata")
    public static String metadata(
        @HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) Optional<String> body,
        @BindingName("name") String queryValue
    ) {
        return body.orElse(queryValue);
    }
}

No exemplo anterior, o queryValue está associado ao parâmetro de cadeia de caracteres de consulta name na URL de solicitação HTTP http://{example.host}/api/metadata?name=test. Aqui está outro exemplo, mostrando como associar a Id de metadados de gatilho de fila.

 @FunctionName("QueueTriggerMetadata")
    public void QueueTriggerMetadata(
        @QueueTrigger(name = "message", queueName = "test-input-java-metadata", connection = "AzureWebJobsStorage") String message,@BindingName("Id") String metadataId,
        @QueueOutput(name = "output", queueName = "test-output-java-metadata", connection = "AzureWebJobsStorage") OutputBinding<TestData> output,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Queue trigger function processed a message: " + message + " with metadaId:" + metadataId );
        TestData testData = new TestData();
        testData.id = metadataId;
        output.setValue(testData);
    }

Observação

O nome fornecido na anotação precisa corresponder à propriedade de metadados.

Contexto de execução

ExecutionContext definido na azure-functions-java-library contém métodos auxiliares para se comunicar com o runtime das funções. Para obter mais informações, consulte o artigo Referência do ExecutionContext.

Agente

Use getLogger, definido em ExecutionContext, para gravar logs do código de função.

Exemplo:


import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.*;

public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        if (req.isEmpty()) {
            context.getLogger().warning("Empty request body received by function " + context.getFunctionName() + " with invocation " + context.getInvocationId());
        }
        return String.format(req);
    }
}

Exibir logs e rastreamento

Você pode usar a CLI do Azure para transmitir o log Java stdout e stderr, bem como outros logs de aplicativo.

Confira como configurar seu aplicativo de funções para gravar o log do aplicativo usando a CLI do Azure:

az webapp log config --name functionname --resource-group myResourceGroup --application-logging true

Para transmitir a saída de log de seu aplicativo de funções usando a CLI do Azure, abra um novo prompt de comando, Bash ou sessão de terminal e digite o seguinte comando:

az webapp log tail --name webappname --resource-group myResourceGroup

O comando az webapp log tail tem opções para filtrar a saída usando a opção --provider.

Para baixar os arquivos de log como um único arquivo ZIP usando a CLI do Azure, abra um novo prompt de comando, Bash ou sessão de terminal e digite o seguinte comando:

az webapp log download --resource-group resourcegroupname --name functionappname

Você deve ter habilitado o log do sistema de arquivos no Portal do Azure ou na CLI do Azure antes de executar esse comando.

Variáveis de ambiente

Em funções, configurações do aplicativo, como conexão de serviço cadeias de caracteres, são expostas como variáveis de ambiente durante a execução. Você pode acessar essas configurações usando System.getenv("AzureWebJobsStorage").

O exemplo a seguir obtém a configuração de aplicativo, com a chave chamada myAppSetting:


public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        context.getLogger().info("My app setting value: "+ System.getenv("myAppSetting"));
        return String.format(req);
    }
}

Usar injeção de dependência em funções Java

O Java Azure Functions dá suporte ao padrão de design de software de injeção de dependência (DI), que é uma técnica para obter a Inversão de Controle (IoC) entre as classes e suas dependências. O Java Azure Functions fornece um gancho para integração com estruturas populares de Injeção de Dependência em seus Aplicativos de Funções. SPI do Java Azure Functions contém uma interface FunctionInstanceInjector. Ao implementar esta interface, você pode retornar uma instância da classe de função e suas funções serão invocadas nessa instância. Isto dá a estruturas como Spring, Quarkus, Google Guice, Dagger etc. a capacidade de criar a instância da função e registrá-la em seu contêiner IOC. Isto significa que você pode usar essas estruturas de Injeção de Dependência para gerenciar suas funções naturalmente.

Observação

Tipos de SPI de Java para o Microsoft Azure Functions(azure-function-java-spi) é um pacote que contém todas as interfaces de SPI para que terceiros interajam com o runtime do Microsoft Azure Functions.

Instância da função injetor para injeção de dependência

azure-function-java-spi contém uma interface FunctionInstanceInjector

package com.microsoft.azure.functions.spi.inject; 

/** 

 * The instance factory used by DI framework to initialize function instance. 

 * 

 * @since 1.0.0 

 */ 

public interface FunctionInstanceInjector { 

    /** 

     * This method is used by DI framework to initialize the function instance. This method takes in the customer class and returns 

     * an instance create by the DI framework, later customer functions will be invoked on this instance. 

     * @param functionClass the class that contains customer functions 

     * @param <T> customer functions class type 

     * @return the instance that will be invoked on by azure functions java worker 

     * @throws Exception any exception that is thrown by the DI framework during instance creation 

     */ 

    <T> T getInstance(Class<T> functionClass) throws Exception; 

} 

Para obter mais exemplos que usam FunctionInstanceInjector para se integrar-se a estruturas de injeção de dependência, consulte este repositório.

Próximas etapas

Para obter mais informações sobre o desenvolvimento de Java do Azure Functions confira os seguintes recursos: