Guía de Azure Functions para desarrolladores de Java
Esta guía contiene información detallada para ayudarle a desarrollar correctamente Azure Functions con Java.
Como desarrollador de Java, si no conoce bien Azure Functions, considere la posibilidad de leer primero uno de los artículos siguientes:
Introducción | Conceptos | Escenarios y ejemplos |
---|---|---|
Fundamentos de las funciones de Java
Una función Java es un método public
decorado con la anotación @FunctionName
. Este método define la entrada de una función Java, y debe ser única en un paquete determinado. El paquete puede tener varias clases con varios métodos públicos anotados con @FunctionName
. Se implementa un único paquete en una aplicación de funciones en Azure. En Azure, la aplicación de funciones proporciona el contexto de implementación, ejecución y administración de las funciones individuales de Java.
Modelo de programación
Los conceptos de desencadenadores y enlaces son fundamentales en Azure Functions. Los desencadenadores inician la ejecución del código. Los enlaces, por otro lado, proporcionan una manera de pasar y devolver datos de una función, sin tener que escribir un código de acceso a datos personalizados.
Creación de funciones de Java
Para facilitar la creación de funciones de Java, hay herramientas y arquetipos basados en Maven que usan plantillas de Java predefinidas para ayudarle a crear proyectos con un desencadenador de funciones específico.
Herramientas basadas en Maven
Los siguientes entornos del desarrollador tienen herramientas de Azure Functions que le permiten crear proyectos de aplicación de una función de Java:
En estos artículos se muestra cómo crear sus primeras funciones mediante el IDE que prefiera.
Creación del scaffolding del proyecto
Si prefiere el desarrollo de la línea de comandos del terminal, la forma más sencilla de crear el scaffolding de los proyectos de aplicación de una función basados en Java es usar arquetipos Apache Maven
. El arquetipo Maven de Java para Azure Functions está publicado en groupId:artifactId: com.microsoft.azure:azure-functions-archetype.
El siguiente comando genera un nuevo proyecto de función de Java con este arquetipo:
mvn archetype:generate \
-DarchetypeGroupId=com.microsoft.azure \
-DarchetypeArtifactId=azure-functions-archetype
Para empezar a usar este arquetipo, consulte el inicio rápido de Java.
Estructura de carpetas
Esta es la estructura de carpetas de un proyecto de Java de 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
Puede usar un archivo host.json compartido para configurar la aplicación de funciones. Cada función tiene su propio archivo de código (.java) y archivo de configuración de enlace (function.json).
Puede tener más de una función en un proyecto. Sin embargo, no ponga sus funciones en archivos JAR independientes. No se admite el uso de varios archivos JAR en una sola aplicación de funciones. FunctionApp
en el directorio de destino es lo que se implementa en la aplicación de funciones en Azure.
Desencadenadores y anotaciones
Las funciones se invocan mediante un desencadenador, como una solicitud HTTP, un temporizador o una actualización de datos. La función debe procesar ese desencadenador y las demás entradas para generar una o más salidas.
Utilice las anotaciones de Java incluidas en el paquete com.microsoft.azure.functions.annotation.* para enlazar las entradas y salidas a los métodos. Para más información, vea los documentos de referencia de Java.
Importante
Debe configurar una cuenta de Azure Storage en local.settings.json para ejecutar de manera local los desencadenadores de Azure Blob Storage, Azure Queue Storage o Azure Table Storage.
Ejemplo:
public class Function {
public String echo(@HttpTrigger(name = "req",
methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS)
String req, ExecutionContext context) {
return String.format(req);
}
}
Aquí está el elemento function.json
correspondiente que generó 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"
}
]
}
Versiones de Java
La versión de Java en la que se ejecutan las funciones en Azure se especifica en el archivo pom.xml. En la actualidad, el arquetipo de Maven genera un archivo pom.xml para Java 8, que se puede cambiar antes de publicar. La versión de Java en el archivo pom.xml debe coincidir con la versión en la que ha desarrollado y probado localmente la aplicación.
Versiones compatibles
En la tabla siguiente se muestran las versiones actuales de Java compatibles para cada versión principal del runtime de Functions, por sistema operativo:
Versión de Functions | Versiones de Java (Windows) | Versiones de Java (Linux) |
---|---|---|
4.x | 17 11 8 |
21 (versión preliminar) 17 11 8 |
3.x | 11 8 |
11 8 |
2.x | 8 | N/D |
A menos que especifique una versión de Java para la implementación, el arquetipo de Maven usa como valor predeterminado Java 8 durante la implementación en Azure.
Especificación de la versión de implementación
Puede controlar la versión de Java de destino del arquetipo de Maven mediante el parámetro -DjavaVersion
. El valor de este parámetro puede ser 8
, 11
, 17
o 21
.
El arquetipo de Maven genera un archivo pom.xml que tiene como destino la versión de Java especificada. Los siguientes elementos del archivo pom.xml indican la versión de Java que se va a usar:
Elemento | Valor de Java 8 | Valor de Java 11 | Valor de Java 17 | Valor de Java 21 (versión preliminar, Linux) | Descripción |
---|---|---|---|---|---|
Java.version |
1.8 | 11 | 17 | 21 | Versión de Java que usa el complemento maven-compiler-plugin. |
JavaVersion |
8 | 11 | 17 | 21 | Versión de Java hospedada por la aplicación de funciones en Azure. |
En los siguientes ejemplos se muestra la configuración de Java 8 en las secciones correspondientes del archivo 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
Debe tener la variable de entorno JAVA_HOME establecida correctamente en el directorio JDK que se usa durante la compilación de código mediante Maven. Asegúrese de que la versión del JDK sea al menos tan alta como la configuración de Java.version
.
Especificación del sistema operativo de implementación
Maven también le permite especificar el sistema operativo en el que se ejecuta la aplicación de funciones en Azure. Use el elemento os
para elegir el sistema operativo.
Elemento | Windows | Linux | Docker |
---|---|---|---|
os |
windows |
linux |
docker |
En el ejemplo siguiente se muestra la configuración del sistema operativo en la sección runtime
del archivo 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>
Disponibilidad y soporte técnico del entorno de ejecución de JDK
Microsoft y las compilaciones Adoptium de OpenJDK se proporcionan y admiten en Functions para Java 8 (Adoptium), Java 11, 17 y 21(MSFT). Estos archivos binarios se proporcionan como una distribución sin costo, multiplataforma y lista para producción de OpenJDK para Azure. Contienen todos los componentes para la creación y ejecución de aplicaciones de Java SE.
Para desarrollo o pruebas locales, puede descargar la compilación de Microsoft de OpenJDK o los archivos binarios de Adoptium Teium de forma gratuita. El soporte técnico de Azure para problemas con los JDK y las aplicaciones de funciones está disponible con un plan de soporte técnico cualificado.
Si quiere seguir usando los archivos binarios de Zulu para Azure en la aplicación de funciones, configure la aplicación en consecuencia. Puede seguir usando los archivos binarios de Azul para su sitio. Pero las mejoras o actualizaciones de seguridad solo están disponibles en las versiones nuevas de OpenJDK. Por este motivo, debería acabar quitando configuración para que las aplicaciones usen la versión más reciente disponible de Java.
Personalización de JVM
Functions permite personalizar la Máquina virtual Java (JVM) utilizada para ejecutar funciones Java. Las siguientes opciones de JVM se usan de forma predeterminada:
-XX:+TieredCompilation
-XX:TieredStopAtLevel=1
-noverify
-Djava.net.preferIPv4Stack=true
-jar
Puede proporcionar otros argumentos a la JVM mediante una de las configuraciones de la aplicación siguientes, en función del tipo de plan:
Tipo de plan | Nombre del valor | Comentario |
---|---|---|
Plan de consumo | languageWorkers__java__arguments |
Esta configuración aumenta los tiempos de inicio en frío para las funciones de Java que se ejecutan en un plan de Consumo. |
Plan Premium Plan dedicado |
JAVA_OPTS |
En las secciones siguientes se muestra cómo agregar esta configuración. Para más información sobre cómo usar la configuración de la aplicación, vea la sección Uso de la configuración de la aplicación.
Portal de Azure
En Azure Portal, utilice la pestaña Configuración de la aplicación para agregar la configuración languageWorkers__java__arguments
o JAVA_OPTS
.
Azure CLI
Puede usar el comando az functionapp config appsettings set a fin de agregar esta configuración, tal como se muestra en el ejemplo siguiente para la opción -Djava.awt.headless=true
:
az functionapp config appsettings set \
--settings "languageWorkers__java__arguments=-Djava.awt.headless=true" \
--name <APP_NAME> --resource-group <RESOURCE_GROUP>
En este ejemplo se habilita el modo "desatendido". Reemplace <APP_NAME>
por el nombre de su aplicación de funciones y <RESOURCE_GROUP>
, por el grupo de recursos.
Bibliotecas de terceros
Azure Functions admite el uso de bibliotecas de terceros. De forma predeterminada, todas las dependencias especificadas en el archivo pom.xml
del proyecto se incluyen automáticamente durante el objetivo de mvn package
. Para las bibliotecas no especificadas como dependencias en el archivo pom.xml
, colóquelas en un directorio lib
del directorio raíz de la función. Las dependencias colocadas en el directorio lib
se agregan al cargador de clases del sistema en tiempo de ejecución.
La dependencia com.microsoft.azure.functions:azure-functions-java-library
se proporciona en la ruta de acceso de la clase de forma predeterminada y no debe incluirse en el directorio lib
. Asimismo, azure-functions-java-worker agrega a la ruta de clase las dependencias que figuran aquí.
Compatibilidad con tipos de datos
Puede usar objetos de Java POJO (Plain Old Java Object), tipos definidos en azure-functions-java-library
o tipos de datos primitivos como String e Integer para enlazar con enlaces de entrada o salida.
POJO
Para convertir los datos de entrada a POJO, azure-functions-java-worker usa la biblioteca gson. Los tipos de POJO que se usan como entradas para las funciones deben ser public
.
Datos binarios
Enlace entradas o salidas binarias a byte[]
; para ello, establezca el campo dataType
en 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");
}
Si espera valores null, use Optional<T>
.
Enlaces
Los enlaces de entrada y de salida permiten conectarse de manera declarativa a los datos desde el código. Cada función puede tener varios enlaces de entrada y de salida.
Ejemplo de enlace 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;
}
}
}
Esta función se invoca con una solicitud HTTP.
- La carga útil de la solicitud HTTP se pasa como
String
para el argumentoinputReq
. - Una entrada se recupera de Table Storage y se pasa como
TestInputData
al argumentoinputData
.
Para recibir un lote de entradas, puede enlazarse a String[]
, POJO[]
, List<String>
o 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;
}
Esta función se desencadena cuando hay nuevos datos en el centro de eventos configurado. Como cardinality
se establece en MANY
, la función recibe un lote de mensajes desde el centro de eventos. El elemento EventData
del centro de eventos se convierte a TestEventData
para la ejecución de la función.
Ejemplo de enlace de salida
Puede enlazar un enlace de salida al valor de retorno con $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;
}
}
Si hay varios enlaces de salida, use el valor devuelto para solo uno de ellos.
Para enviar varios valores de salida, use el tipo OutputBinding<T>
que se define en el paquete 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;
}
Invoque esta función en un objeto HttpRequest
. Escribe varios valores en Queue Storage.
HttpRequestMessage y HttpResponseMessage
Se definen en azure-functions-java-library
. Son tipos de asistente que funcionan con funciones HttpTrigger.
Tipo especializado | Destino | Uso típico |
---|---|---|
HttpRequestMessage<T> |
Desencadenador HTTP | Obtiene el método, encabezados o consultas. |
HttpResponseMessage |
Enlace de salida HTTP | Devuelve un estado distinto de 200. |
Metadatos
Algunos desencadenadores envían metadatos de desencadenador junto con datos de entrada. Puede usar la anotación @BindingName
para enlazarla a metadatos de desencadenador.
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);
}
}
En el ejemplo anterior, el elemento queryValue
está enlazado al parámetro de cadena de consulta name
de la dirección URL de solicitud HTTP http://{example.host}/api/metadata?name=test
. Este es otro ejemplo que muestra cómo enlazar a Id
desde los metadatos de desencadenador de la cola.
@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
El nombre que se proporciona en la anotación debe coincidir con la propiedad de metadatos.
Contexto de ejecución
El elemento ExecutionContext
definido en azure-functions-java-library
contiene métodos de ayuda que le permitirán comunicarse con las funciones en tiempo de ejecución. Para obtener más información, consulte el artículo de referencia sobre ExecutionContext.
Registrador
Use el elemento getLogger
definido en ExecutionContext
para escribir registros desde el código de función.
Ejemplo:
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);
}
}
Ver registros y seguimiento
Puede usar la CLI de Azure para hacer streaming del registro stdout y stderr de Java, así como de otros registros de aplicaciones.
Aquí se explica cómo configurar la aplicación de funciones para escribir el registro de aplicación con la CLI de Azure:
az webapp log config --name functionname --resource-group myResourceGroup --application-logging true
Para transmitir en secuencias la salida para la aplicación de funciones con la CLI de Azure, abra una nueva sesión del símbolo del sistema, Bash o Terminal y escriba el siguiente comando:
El comando az webapp log tail tiene opciones para filtrar la salida usando la opción --provider
.
Para descargar los archivos de registro como un solo archivo ZIP mediante la CLI de Azure, abra una sesión nueva del símbolo del sistema, Bash o Terminal y escriba el siguiente comando:
az webapp log download --resource-group resourcegroupname --name functionappname
Debe haber habilitado el registro del sistema de archivos en Azure Portal o la CLI de Azure antes de ejecutar este comando.
Variables de entorno
En Functions, la configuración de la aplicación, como las cadenas de conexión del servicio, se exponen como variables de entorno durante la ejecución. Puede obtener acceso a estas opciones mediante System.getenv("AzureWebJobsStorage")
.
En el siguiente ejemplo se obtiene la configuración de la aplicación, con la clave denominada 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);
}
}
Uso de la inserción de dependencias en Java Functions
Azure Functions Java admite el modelo de diseño de software de inserción de dependencias (DI), que es una técnica para lograr la inversión de control (IoC) entre las clases y sus dependencias. Java Azure Functions proporciona un enlace para integrarse con marcos populares de inserción de dependencias en las aplicaciones de Functions. SPI de Azure Functions Java contiene una interfaz FunctionInstanceInjector. Al implementar esta interfaz, puede devolver una instancia de la clase de función y las funciones se invocarán en esta instancia. Esto proporciona a marcos como Spring, Quarkus, Google Guice, Dagger, etc. la capacidad de crear la instancia de función y registrarla en su contenedor de IOC. Esto significa que puede usar esos marcos de inserción de dependencias para administrar las funciones de forma natural.
Nota
Tipos de SPI de Microsoft Azure Functions Java (azure-function-java-spi) es un paquete que contiene todas las interfaces SPI para que terceros puedan interactuar con el tiempo de ejecución de las funciones de Microsoft Azure.
Inserción de instancias de funciones para la inserción de dependencias
azure-function-java-spi contiene una interfaz 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 obtener más ejemplos que usan FunctionInstanceInjector para integrarse con marcos de inserción de dependencias, consulte este repositorio.
Pasos siguientes
Para más información sobre el desarrollo de Java de Azure Functions, vea los siguientes recursos:
- Procedimientos recomendados para Azure Functions
- Referencia para desarrolladores de Azure Functions
- Enlaces y desencadenadores de Azure Functions
- Desarrollo y depuración local con Visual Studio Code, IntelliJ y Eclipse
- Depuración remota de funciones de Java con Visual Studio Code
- Complemento Maven para Azure Functions
- Optimización de la creación de funciones a través del objetivo
azure-functions:add
y preparación de un directorio de almacenamiento provisional para la implementación del archivo ZIP.