Guide des développeurs Java sur Azure Functions

Ce guide contient des informations détaillées permettant de développer des fonctions Azure en Java.

Si vous êtes développeur Java et que vous débutez avec Azure Functions, lisez d’abord l’un des articles suivants :

Prise en main Concepts Scénarios/exemples

Informations de base sur les fonctions Java

Une fonction Java est une méthode public dotée de l’annotation @FunctionName. Cette méthode définit l’entrée d’une fonction Java et doit être unique dans un package particulier. Le package peut comporter plusieurs classes avec plusieurs méthodes publiques annotées par @FunctionName. Un seul package est déployé dans une application de fonction dans Azure. Dans Azure, l’application de fonction donne le contexte de déploiement, d’exécution et de gestion des différentes fonctions Java.

Modèle de programmation

Les concepts de déclencheurs et liaisons sont fondamentaux pour Azure Functions. Les déclencheurs démarrent l’exécution de votre code. Les liaisons vous permettent de transmettre des données et de retourner des données à partir d’une fonction, sans avoir à écrire du code d’accès aux données personnalisées.

Créer des fonctions Java

Pour faciliter la création de fonctions Java, il existe des outils basés sur Maven et des archétypes qui utilisent des modèles Java prédéfinis pour permettre de créer des projets avec un déclencheur de fonction spécifique.

Outils basés sur Maven

Les environnements de développement suivants comportent des outils Azure Functions qui permettent de créer des projets de fonctions Java :

Ces articles vous montrent comment créer vos premières fonctions à l’aide de l’environnement IDE de votre choix.

Génération de modèles automatique d’un projet

Si vous préférez le développement en ligne de commande dans le terminal, le moyen le plus simple de définir la structure de projets de fonctions Java consiste à utiliser des archétypes Apache Maven. L’archétype Java Maven pour Azure Functions est publié sous les groupId:artifactId suivants : com.microsoft.azure:azure-functions-archetype.

La commande suivante génère un projet de fonction Java à l’aide de cet archétype :

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

Pour apprendre à utiliser cet archétype, voir le démarrage rapide Java.

Structure de dossiers

Voici la structure de dossiers d’un projet Java 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

Vous pouvez utiliser un fichier host.json partagé pour configurer l’application de fonction. Chaque fonction a son propre fichier de code (.java) et un fichier de configuration de liaison (function.json).

Vous pouvez placer plusieurs fonctions dans un projet. Évitez de placer vos fonctions dans des fichiers JAR distincts. L’élément FunctionApp dans le répertoire cible correspond à ce qui est déployé dans votre application de fonction dans Azure.

Déclencheurs et annotations

Les fonctions sont appelées par un déclencheur, comme une requête HTTP, un minuteur ou une mise à jour des données. Votre fonction doit traiter ce déclencheur et toutes les autres entrées, puis produire une ou plusieurs sorties.

Utilisez les annotations Java incluses dans le package com.microsoft.azure.functions.annotation.* pour lier des entrées et des sorties à vos méthodes. Pour plus d’informations, consultez les Docs de référence Java.

Important

Vous devez configurer un compte de stockage Azure dans votre fichier local.settings.json pour exécuter les déclencheurs de Stockage Blob Azure, Stockage File d’attente Azure et Stockage Table Azure en local.

Exemple :

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

Voici le fichier function.json correspondant généré par 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"
    }
  ]
}

Versions Java

La version de Java sur laquelle votre application s’exécute dans Azure est spécifiée dans le fichier pom.xml. L’archétype Maven génère actuellement un fichier pom.xml pour Java 8, que vous pouvez modifier avant la publication. La version de Java indiquée dans le fichier pom.xml doit correspondre à celle sur laquelle vous avez développé et testé votre application en local.

Versions prises en charge

Le tableau suivant présente les versions Java actuellement prises en charge pour chaque version majeure du runtime Functions, par système d’exploitation :

Version de Functions Versions Java (Windows) Versions Java (Linux)
4.x 17
11
8
21 (Preview)
17
11
8
3.x 11
8
11
8
2.x 8 n/a

Si vous n’avez pas spécifié de version Java pour votre déploiement, l’archétype Maven par défaut est Java 8 pendant le déploiement sur Azure.

Spécification de la version du déploiement

Vous pouvez contrôler la version de Java ciblée par l’archétype Maven à l’aide du paramètre -DjavaVersion. La valeur de ce paramètre peut être 8, 11, 17 ou 21.

L’archétype Maven génère un fichier pom.xml qui cible la version spécifiée de Java. Les éléments suivants du fichier pom.xml indiquent quelle version de Java utiliser :

Élément Valeur Java 8 Valeur Java 11 Valeur Java 17 Valeur Java 21 (préversion, Linux) Description
Java.version 1.8 11 17 21 Version Java utilisée par le plug-in Compilateur Maven.
JavaVersion 8 11 17 21 Version Java hébergée par l’application de fonction dans Azure.

Les exemples suivants illustrent les paramètres de Java 8 dans les sections correspondantes du fichier 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>

Important

La variable d’environnement JAVA_HOME doit être définie correctement dans le répertoire JDK utilisé lors de la compilation du code à l’aide de Maven. La version du JDK doit être au moins aussi élevée que le paramètre Java.version.

Spécification du système d’exploitation du déploiement

Maven offre également la possibilité de spécifier le système d’exploitation sur lequel l’application de fonction s’exécute dans Azure. Utilisez l’élément os pour choisir le système d’exploitation.

Élément Windows Linux Docker
os windows linux docker

L’exemple suivant montre le paramètre de système d’exploitation dans la section runtime du fichier 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>

Disponibilité et prise en charge du runtime JDK

Microsoft et Adoptium builds d’OpenJDK sont fournies et prises en charge sur Functions pour Java 8 (Adoptium), Java 11, 17 et 21 (MSFT). Ces fichiers binaires sont fournis en tant que distribution d’OpenJDK pour Azure sans coût, multiplateforme et prête pour la production. Elles contiennent tous les composants nécessaires pour générer et exécuter des applications Java SE.

Pour le développement ou les tests locaux, vous pouvez télécharger gratuitement la version Microsoft d’OpenJDK ou Adoptium Temurin. Le support Azure des problèmes avec les JDK et les applications de fonction est disponible avec dans un plan de support qualifié.

Si vous souhaitez continuer à utiliser les fichiers binaires Zulu pour Azure sur votre application de fonction, configurez votre application en conséquence. Vous pouvez continuer à utiliser les fichiers binaires Azul pour votre site. Toutefois, les correctifs de sécurité ou les améliorations ne sont disponibles que dans les nouvelles versions d’OpenJDK. C’est pourquoi vous devrez, au final, supprimer cette configuration afin que vos applications utilisent la dernière version disponible de Java.

Personnaliser une machine virtuelle Java

Azure Functions vous permet de personnaliser la machine virtuelle Java (JVM) utilisée pour exécuter vos fonctions Java. Les options de machine virtuelle Java suivantes sont utilisées par défaut :

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

Vous pouvez fournir d’autres arguments à la machine virtuelle Java en utilisant l’un des paramètres d’application suivants, en fonction du type de plan :

Type de plan Nom du paramètre Commentaire
Plan Consommation languageWorkers__java__arguments Ce paramètre augmente les temps de démarrage à froid pour les fonctions Java exécutées dans un plan de consommation.
Plan Premium
Plan dédié
JAVA_OPTS

Les sections suivantes vous montrent comment ajouter ces paramètres. Pour en savoir plus sur l’utilisation des paramètres d’application, consultez la section Utiliser les paramètres d’application.

Portail Azure

Dans le portail Azure, utilisez l’onglet Paramètres d’application pour ajouter languageWorkers__java__arguments ou le paramètre JAVA_OPTS.

Azure CLI

Vous pouvez utiliser la commande az functionapp config appsettings set pour ajouter ces paramètres, comme illustré dans l’exemple suivant pour l’option -Djava.awt.headless=true :

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

Cet exemple active le mode « headless ». Remplacez également <APP_NAME> par le nom de votre application de fonction, et <RESOURCE_GROUP> par le groupe de ressources.

Bibliothèques tierces

Azure Functions prend en charge l’utilisation de bibliothèques tierces. Par défaut, toutes les dépendances spécifiées dans votre fichier pom.xml de projet sont automatiquement regroupées pendant l’objectif mvn package. Pour les bibliothèques qui ne sont pas définies comme des dépendances dans le fichier pom.xml, placez-les dans un répertoire lib du dossier racine de la fonction. Les dépendances placées dans le répertoire lib sont ajoutées au chargeur de classes système lors de l’exécution.

La dépendance com.microsoft.azure.functions:azure-functions-java-library est fournie sur le chemin de classe par défaut et ne doit pas être incluse dans le répertoire lib. En outre, azure-fonctions-java-worker ajoute les dépendances listées ici à l’instruction classpath.

Prise en charge des types de données

Vous pouvez utiliser des objets Plain old Java (POJO), des types définis dans azure-functions-java-library ou des types de données dataTypes primitifs comme chaîne et comme entier à lier à des liaisons d’entrée/de sortie.

POJO

Pour la conversion des données d’entrée en POJO, azure-fonctions-java-worker utilise une bibliothèque gson. Les types POJO utilisés comme entrées de fonctions doivent être public.

Données binaires

Liez des entrées et sorties binaires à byte[] en définissant le champ dataType dans votre fichier function.json sur 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");
    }

Si vous attendez des valeurs null, utilisez Optional<T>.

Liaisons

Les liaisons d’entrée et de sortie fournissent un moyen déclaratif de se connecter à des données à partir de votre code. Une fonction peut avoir plusieurs liaisons d’entrée et de sortie.

Exemple de liaison d’entrée

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;
        }
    }
}

Vous appelez cette fonction avec une requête HTTP.

  • La charge utile de requête HTTP est transmise comme une String pour l’argument inputReq.
  • Une entrée est récupérée à partir du Stockage Table et est transmise comme TestInputData à l’argument inputData.

Pour recevoir un lot d’entrées, vous pouvez lier à 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;
}

Cette fonction est déclenchée chaque fois qu’il existe de nouvelles données dans le hub d’événements configuré. Comme la cardinality est définie sur MANY, la fonction reçoit un lot de messages du hub d’événements. Les EventData de l’Event Hub sont converties en TestEventData pour l’exécution de la fonction.

Exemple de liaison de sortie

Vous pouvez lier une liaison de sortie à la valeur de retour avec $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;
    }
}

S’il existe plusieurs liaisons de sortie, utilisez la valeur de retour pour un seul d’entre eux.

Pour envoyer plusieurs valeurs de sortie, utilisez la OutputBinding<T> définie dans le package 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;
    }

Vous appelez cette fonction avec un objet HttpRequest. Elle écrit plusieurs valeurs dans Stockage File d’attente.

HttpRequestMessage et HttpResponseMessage

Elles sont définies dans azure-functions-java-library. Il existe des types d’assistance pour utiliser les fonctions HttpTrigger.

Type spécialisé Cible Utilisation classique
HttpRequestMessage<T> Déclencheur HTTP Obtention des requêtes, en-têtes et méthodes
HttpResponseMessage Liaison de sortie HTTP Renvoi de statut autre que 200

Métadonnées

Quelques déclencheurs envoient des métadonnées de déclencheur ainsi que des données d’entrée. Vous pouvez utiliser une annotation @BindingName à lier à des métadonnées de déclencheur.

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);
    }
}

Dans l’exemple précédent, la valeur queryValue est liée au paramètre de chaîne de requête name dans l’URL de requête HTTP http://{example.host}/api/metadata?name=test. Voici un autre exemple de liaison à Id à partir des métadonnées de déclencheur de file d’attente.

 @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);
    }

Notes

Le nom fourni dans l’annotation doit correspondre à la propriété des métadonnées.

Contexte d’exécution

Le contenu ExecutionContext défini dans la bibliothèque azure-functions-java-library contient des méthodes d’assistance pour communiquer avec l’environnement d’exécution des fonctions. Pour plus d’informations, consultez l’article de référence sur ExecutionContext.

Enregistreur

Utilisez getLogger, défini dans ExecutionContext, pour écrire des journaux d’activité à partir du code de fonction.

Exemple :


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);
    }
}

Afficher les journaux d’activité et le suivi

Vous pouvez utiliser l’interface Azure CLI pour diffuser en continu la journalisation Java stdout et stderr, et toute autre journalisation d’application.

Voici comment configurer votre application de fonction pour écrire la journalisation d’application en utilisant Azure CLI :

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

Pour obtenir une sortie de journalisation pour votre application de fonction en utilisant Azure CLI, ouvrez une nouvelle session d’invite de commandes, Bash ou Terminal, puis entrez la commande suivante :

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

La commande az webapp log tail permet de filtrer la sortie en utilisant l’option --provider.

Pour télécharger les fichiers journaux sous la forme d’un fichier zip unique en utilisant Azure CLI, ouvrez une nouvelle session d’invite de commandes, Bash ou Terminal, puis entrez la commande suivante :

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

Avant d’exécuter cette commande, vous devez avoir activé la journalisation du système de fichiers dans le portail Azure ou dans Azure CLI.

Variables d'environnement

Dans Functions, les paramètres de l’application, par exemple, les chaînes de connexion de service, sont exposées en tant que variables d’environnement pendant l’exécution. Vous pouvez accéder à ces paramètres avec System.getenv("AzureWebJobsStorage").

L’exemple suivant obtient le paramètre d’application, avec la clé nommée 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);
    }
}

Utiliser l’injection de dépendances dans Java Functions

Azure Functions Java prend en charge le modèle de conception logicielle d’injection de dépendances, qui est une technique pour obtenir une inversion de contrôle entre les classes et leurs dépendances. Java Azure Functions fournit un hook pour s’intégrer aux infrastructures d’injection de dépendances populaires dans vos applications Functions. Azure Functions SPI Java contient une interface FunctionInstanceInjector. En implémentant cette interface, vous pouvez retourner une instance de votre classe de fonction et vos fonctions seront appelées sur cette instance. Cela donne des frameworks comme Spring, Quarkus, Google Guice, Dagger, etc. la possibilité de créer la fonction instance et de l’inscrire dans leur conteneur IOC. Cela signifie que vous pouvez utiliser ces infrastructures d’injection de dépendances pour gérer vos fonctions naturellement.

Notes

Microsoft Azure Functions Types SPI Java (azure-function-java-spi) est un package qui contient toutes les interfaces SPI permettant à des tiers d’interagir avec le runtime Microsoft Azure Functions.

Injecteur instance de fonction pour l’injection de dépendances

azure-function-java-spi contient une 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; 

} 

Pour plus d’exemples qui utilisent FunctionInstanceInjector pour s’intégrer aux infrastructures d’injection de dépendances, consultez ce dépôt.

Étapes suivantes

Pour plus d’informations sur le développement Java Azure Functions, voir les ressources suivantes :