Guia do desenvolvedor Java do Azure Functions

Este guia contém informações detalhadas para ajudá-lo a desenvolver com êxito o Azure Functions usando Java.

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

Introdução Conceitos Cenários/exemplos

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

Uma função Java é um public método, decorado com a anotação @FunctionName. Este 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ção no Azure. No Azure, o aplicativo de função fornece o contexto de implantação, execução e gerenciamento para suas funções Java individuais.

Modelo de programação

Os conceitos de gatilhos e ligações são fundamentais para o Azure Functions. Os gatilhos iniciam a execução do seu código. As ligações oferecem uma maneira de passar e retornar dados de uma função, sem ter que escrever código de acesso a dados personalizado.

Criar funções Java

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

Ferramentas baseadas em Maven

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

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

Projeto andaimes

Se você preferir o desenvolvimento de linha de comando a partir do Terminal, a maneira mais simples de criar projetos de função baseados em Java é usar Apache Maven arquétipos. O arquétipo Java Maven para Azure Functions é publicado sob o seguinte groupId:artifactId: com.microsoft.azure:azure-functions-archetype.

O comando a seguir 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, consulte o Guia de 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 de host.json compartilhado para configurar o aplicativo de função. Cada função tem seu próprio arquivo de código (.java) e arquivo de configuração de vinculação (function.json).

Você pode colocar mais de uma função em um projeto. Evite colocar suas funções em frascos separados. O FunctionApp no diretório de destino é o que é implantado em seu aplicativo de função 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 de 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 vincular entradas e saídas aos seus métodos. Para obter mais informações, consulte os documentos de referência Java.

Importante

Você deve configurar uma conta de Armazenamento do Azure em seu local.settings.json para executar o armazenamento de Blob do Azure, o armazenamento de Filas do Azure ou gatilhos de armazenamento de Tabela do Azure localmente.

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 correspondente function.json 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 Java

A versão do Java na qual 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 de publicar. A versão Java no pom.xml deve corresponder à versão na qual você desenvolveu e testou localmente seu aplicativo.

Versões suportadas

A tabela a seguir mostra as versões atuais suportadas do Java para cada versão principal do tempo de execução do Functions, por sistema operacional:

Versão das funções Versões Java (Windows) Versões Java (Linux)
4.x 17
11
8
21 (Pré-visualização)
17
11
8
3.x 11
8
11
8
2.x 8 n/d

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

Especificar a versão de implantação

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

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

Elemento Valor Java 8 Valor Java 11 Valor Java 17 Valor Java 21 (Preview, Linux) Description
Java.version 1.8 11 17 21 Versão do Java usada pelo maven-compiler-plugin.
JavaVersion 8 11 17 21 Versão Java hospedada pelo aplicativo de função no Azure.

Os exemplos a seguir mostram as configurações do 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 JDK 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 alta quanto a Java.version configuração.

Especificar o SO de implementação

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

Elemento Windows Linux Docker
os windows linux docker

O exemplo a seguir mostra a configuração do sistema operacional na runtime seção 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>

Disponibilidade e suporte ao tempo de execução do JDK

As compilações Microsoft e Adoptium do OpenJDK são fornecidas e suportadas no Functions for Java 8 (Adoptium), Java 11, 17 e 21 (MSFT). Esses binários são fornecidos como uma distribuição gratuita, multiplataforma e pronta para produção do OpenJDK para Azure. Contêm todos os componentes para compilar e executar aplicações Java SE.

Para desenvolvimento local ou testes, você pode baixar a compilação da Microsoft dos binários OpenJDK ou Adoptium Temurin gratuitamente. O suporte do Azure para problemas com os JDKs e aplicativos de função está disponível com um plano de suporte qualificado.

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

Personalizar a JVM

O Functions permite personalizar a máquina virtual Java (JVM) usada para executar suas funções Java. As seguintes opções da 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 do aplicativo, dependendo do tipo de plano:

Tipo de plano Nome da definiçã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 configurações de aplicativos, consulte a seção Trabalhar com configurações de aplicativos.

Portal do Azure

No portal do Azure, use a guia Configurações do Aplicativo para adicionar a ou a JAVA_OPTSlanguageWorkers__java__argumentsconfiguração.

CLI do Azure

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

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 headless. Substitua <APP_NAME> pelo nome do seu aplicativo de função e <RESOURCE_GROUP> pelo grupo de recursos.

Bibliotecas de terceiros

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

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

Suporte a tipos de dados

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

POJOs

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

Dados binários

Vincule entradas ou saídas binárias ao byte[], definindo o dataType campo em seu 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>.

Enlaces

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

Exemplo de vinculaçã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 útil da solicitação HTTP é passada como um String para o argumento inputReq.
  • Uma entrada é recuperada do armazenamento de tabela e é passada para TestInputData o argumento inputData.

Para receber um lote de entradas, você pode vincular 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 há novos dados no hub de eventos configurado. Como o cardinality está definido como MANY, a função recebe um lote de mensagens do hub de eventos. EventData do hub de eventos é convertido para para TestEventData a execução da função.

Exemplo de vinculação de saída

Você pode vincular uma associação de saída ao valor de retorno 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 ligações de saída, use o valor de retorno para apenas uma delas.

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

@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 HttpRequest objeto. Ele grava vários valores no armazenamento de filas.

HttpRequestMessage e HttpResponseMessage

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

Tipo especializado Destino Uso típico
HttpRequestMessage<T> Acionador HTTP Obtém método, cabeçalhos ou consultas
HttpResponseMessage Vinculação de saída HTTP Devolve o estado diferente de 200

Metadados

Poucos gatilhos enviam metadados de gatilho junto com dados de entrada. Você pode usar a anotação @BindingName para vincular para acionar metadados.

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á vinculado ao parâmetro name de cadeia de caracteres de consulta na URL de solicitação HTTP, http://{example.host}/api/metadata?name=test. Aqui está outro exemplo, mostrando como vincular a Id 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);
    }

Nota

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

Contexto de execução

ExecutionContext, definido no azure-functions-java-library, contém métodos auxiliares para se comunicar com o tempo de execução das funções. Para obter mais informações, consulte o artigo de referência ExecutionContext.

Registador

Use getLogger, definido em ExecutionContext, para gravar logs a partir do código da 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 do Java stdout e stderr e outros logs de aplicativos.

Veja como configurar seu aplicativo de função para escrever 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 para seu aplicativo de função usando a CLI do Azure, abra um novo prompt de comando, Bash ou sessão 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 --provider opção.

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 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, as configurações do aplicativo, como cadeias de conexão de serviço, 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 do 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 no Java Functions

O Azure Functions Java dá suporte ao padrão de design de software de injeção de dependência (DI), que é uma técnica para obter Inversão de Controle (IoC) entre 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 do Functions. O Azure Functions Java SPI contém uma interface FunctionInstanceInjector. Ao implementar essa interface, você pode retornar uma instância de sua classe de função e suas funções serão invocadas nessa instância. Isso dá a frameworks 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. Isso significa que você pode usar essas estruturas de injeção de dependência para gerenciar suas funções naturalmente.

Nota

Microsoft Azure Functions Java SPI Types (azure-function-java-spi) é um pacote que contém todas as interfaces SPI para terceiros interagirem com o tempo de execução das funções do Microsoft Azure.

Injetor de instância de função 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 integrar com estruturas de injeção de dependência, consulte este repositório.

Próximos passos

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