Azure Functions Java 開發人員指南

此指南包含詳細資訊,可協助您使用 Java 成功開發 Azure Functions。

身為 Java 開發人員,如果您不熟悉 Azure Functions,請考慮先閱讀下列其中一篇文章:

開始使用 概念 案例/範例

Java 函式基本概念

JAVA 函式是 public 方法,以註釋 @FunctionName 裝飾。 此方法定義 JAVA 函式的進入點,在特定封裝中必須是唯一的。 套件可以有多個類別,並具有多個以 @FunctionName 標註的公用方法。 單一套件會部署到 Azure 中的函數應用程式。 在 Azure 中,函數應用程式會提供適用於您個別 Java 函式的部署、執行和管理內容。

程式設計模型

觸發程序和繫結的概念是 Azure Functions 的基礎。 觸發程序會開始執行您的程式碼。 繫結可讓您將資料傳至函式以及從函式傳回資料,而不需要撰寫自訂的資料存取程式碼。

建立 JAVA 函式

為了讓您更輕鬆建立 JAVA 函式,有以 Maven 為基礎的工具和原型使用預先定義的 JAVA 範本,協助您建立具有特定函式觸發程序的專案。

以 Maven 為基礎的工具

下列開發人員環境具有 Azure Functions 工具,可讓您建立 JAVA 函式專案:

上述文章示範如何使用您選擇的 IDE 來建立第一個函式。

專案 Scaffolding

如果您偏好從終端機執行命令列開發,則針對以 JAVA 為基礎的函式專案,使用 Apache Maven 原型來建立結構最簡單。 Azure Functions 的 JAVA Maven 原型已發佈於下列 groupId:artifactId: com.microsoft.azure:azure-functions-archetype 之下。

下列命令使用此原型產生新的 JAVA 函式專案:

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

若要開始使用此原型,請參閱 JAVA 快速入門

資料夾結構

以下是 Azure Functions Java 專案的資料夾結構:

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

您可以使用共用的 host.json 檔案來設定函數應用程式。 每個函式都有自己的程式碼檔案 (.java) 和繫結設定檔 (function.json)。

您可以在專案中放入多個函式。 請勿將函式放入個別的 jar。 目標目錄中的 FunctionApp 會部署至 Azure 中的函數應用程式。

觸發程序和註解

函式由觸發程序叫用,例如 HTTP 要求、計時器或更新資料。 函式必須處理該觸發程序和任何其他輸入,以產生一或多個輸出。

請使用 com.microsoft.azure.functions.annotation.* 套件中所包含的 Java 註釋,以將輸入和輸出繫結至方法。 如需詳細資訊,請參閱 JAVA 參考文件

重要

您必須在 local.settings.json 中設定 Azure 儲存體帳戶,以在本機執行 Azure Blob 儲存體、Azure 佇列儲存體或 Azure 資料表觸發程序。

範例:

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

以下是 azure-functions-maven-plugin 所產生的對應 function.json

{
  "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"
    }
  ]
}

Java 版本

應用程式在 Azure 中執行的 Java 版本是在 pom.xml 檔案中指定。 Maven Archetype 目前會產生適用於 Java 8 的 pom.xml,讓您可在發佈之前變更。 Pom.xml 中的 Java 版本應該會符合您在本機開發並測試應用程式所用的版本。

支援的版本

下表會依作業系統,針對 Functions 執行階段的每個主要版本,顯示目前支援的 Java 版本:

Functions 版本 Java 版本 (Windows) Java 版本 (Linux)
4.x 17
11
8
21 (預覽版)
17
11
8
3.x 11
8
11
8
2.x 8 n/a

除非您為部署指定 Java 版本,否則 Maven Archetype 會在部署到 Azure 期間預設為 Java 8。

指定部署版本

您可以使用 -DjavaVersion 參數來控制 Maven Archetype 的目標 Java 版本。 此參數的值可以是 8111721

Maven Archetype 會產生以指定 Java 版本為目標的 pom.xml。 Pom.xml 中的下列元素會指出要使用的 Java 版本:

Element Java 8 值 Java 11 值 Java 17 值 Java 21 值 (預覽、Linux) 描述
Java.version 1.8 11 17 21 maven-compiler-plugin 所使用的 Java 版本。
JavaVersion 8 11 17 21 Azure 中函數應用程式所裝載的 Java 版本。

下列範例顯示 pom.xml 檔案相關區段中適用於 Java 8 的設定:

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>

重要

您必須將 JAVA_HOME 環境變數正確設定為使用 Maven 進行程式碼編譯期間所使用的 JDK 目錄。 請確定 JDK 的版本至少要與 Java.version 設定相同。

指定部署 OS

Maven 也可讓您指定函數應用程式在 Azure 中執行所在的作業系統。 使用 os 元素來選擇作業系統。

Element Windows Linux Docker
os windows linux docker

下列範例顯示 pom.xml 檔案 runtime 區段中的作業系統設定:

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

JDK 執行階段可用性和支援

適用於 Java 8 (Adoptium)、Java 11、17 和 21 (MSFT) 的 Functions 上提供及支援 OpenJDK 的 Microsoft 和 Adoptium 組建。 這些二進位檔會以無成本、多平台且生產環境就緒的適用於 Azure 的 OpenJDK 發行版本來提供。 其中包含建置及執行 Java SE 應用程式所需的所有元件。

針對本機開發或測試,您可以免費下載 OpenJDK 的 Microsoft 組建Adoptium Temurin (英文) 二進位檔。 合格的支援方案讓您享有 JDK 和函數應用程式相關問題的 Azure 支援

如果您想要在函數應用程式中繼續使用適用於 Azure 的 Zulu 二進位檔,請據以設定應用程式。 您可以繼續使用網站的 Azul 二進位檔。 不過,任何安全性修補檔或改善功能都只能在新版本的 OpenJDK 中使用。 因此,您最終應該移除此設定,如此應用程式就能夠使用最新可用的 Java 版本。

自訂 JVM

函式可讓您自訂用來執行 JAVA 函式的 JAVA 虛擬機器 (JVM)。 預設會使用下列 JVM 選項

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

視方案類型而定,您可以使用下列其中一個應用程式設定,為 JVM 提供其他引數:

方案類型 設定名稱 註解
取用方案 languageWorkers__java__arguments 此設定會延長在使用量方案中執行之 Java 函式的冷啟動時間。
進階方案
專用方案
JAVA_OPTS

下列各節將示範如何新增這些設定。 若要深入了解如何使用應用程式設定,請參閱使用應用程式設定一節。

Azure 入口網站

Azure 入口網站中,使用 [應用程式設定] 索引標籤來新增 languageWorkers__java__argumentsJAVA_OPTS 設定。

Azure CLI

您可以使用 az functionapp config appsettings set 命令來新增這些設定,如 -Djava.awt.headless=true 選項的下列範例所示:

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

此範例啟用無周邊模式。 請將 <APP_NAME> 換成您的函數應用程式名稱,將 <RESOURCE_GROUP> 換成資源群組。

第三方程式庫

Azure Functions 支援使用第三方程式庫。 根據預設,在 mvn package 目標期間,將會自動包裝專案 pom.xml 檔案中指定的所有相依性。 對於在 pom.xml 檔案中未指定為相依性的程式庫,請將其放入函式根目錄的 lib 目錄中。 在執行階段,放在 lib 目錄中的相依性會新增至系統類別載入器。

預設會在類別路徑上提供 com.microsoft.azure.functions:azure-functions-java-library 相依性,因此不需要加入 lib 目錄中。 此外,azure-functions-java-worker 還將這裡列出的相依性新增至類別路徑。

資料類型支援

您可以使用 Plain Old JAVA Object (POJO)、azure-functions-java-library 中定義的類型或基本資料類型 (例如字串和整數),以繫結至輸入或輸出繫結。

POJO

為了將輸入資料轉換成 POJO,azure-functions-java-worker 使用 gson 程式庫。 作為函式輸入的 POJO 類型應為 public

二進位資料

在 function.json 中將 dataType 欄位設定為 binary,以將二進位輸入或輸出繫結至 byte[]

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

如果您預期會有 Null 值,請使用 Optional<T>

繫結

輸入和輸出繫結提供從您的程式碼內連線到資料的宣告式方法。 一個函數可以有多個輸入和輸出繫結。

輸入繫結範例

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

您可以使用 HTTP 要求來叫用此函式。

  • HTTP 要求承載會以 String 形式傳給 inputReq 引數。
  • 系統會從資料表儲存體中取出一個項目,並以 TestInputData 形式傳給 inputData 引數。

若要接收輸入批次,您可以繫結至 String[]POJO[]List<String>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;
}

每當設定的事件中樞內有新資料時,就會觸發此函式。 由於 cardinality 設為 MANY,此函式會從事件中樞收到一批訊息。 來自事件中樞的 EventData 會轉換成 TestEventData 供函式執行。

輸出繫結範例

您可以使用 $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;
    }
}

如果有多個輸出繫結,請只對其中一個使用傳回值。

若要傳送多個輸出值,請使用 azure-functions-java-library 套件中定義的 OutputBinding<T>

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

請在 HttpRequest 物件上叫用此函式。 此函式會將多個值寫入佇列儲存體。

HttpRequestMessage 和 HttpResponseMessage

這些都在 azure-functions-java-library 中定義。 這些也是與 HttpTrigger 函式搭配使用的協助程式類型。

特殊類型 Target 一般使用方式
HttpRequestMessage<T> HTTP 觸發程序 取得方法、標頭或查詢
HttpResponseMessage HTTP 輸出繫結 傳回 200 以外的狀態

中繼資料

有幾個觸發程序會將觸發程序中繼資料與輸入資料一起傳送。 您可以使用註釋 @BindingName 來繫結至觸發程序中繼資料。

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

在上述範例中,queryValue 繫結至 HTTP 要求 URL http://{example.host}/api/metadata?name=test 中的查詢字串參數 name。 以下是另一個範例,示範如何從佇列觸發程序中繼資料繫結至 Id

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

注意

註釋中提供的名稱必須符合中繼資料屬性。

執行內容

azure-functions-java-library 中定義的 ExecutionContext 包含協助程式方法,用來與函式執行階段進行通訊。 如需詳細資訊,請參閱 ExecutionContext 參考文章

記錄器

使用 ExecutionContext 中定義的 getLogger,從函式程式碼寫入記錄。

範例:


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

檢視記錄和追蹤

您可以使用 Azure CLI 來串流 Java stdout 和 stderr 記錄,以及其他應用程式記錄。

以下示範如何使用 Azure CLI,將函式應用程式設定為寫入應用程式記錄:

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

若要使用 Azure CLI 來串流函數應用程式的記錄輸出,請開啟新的命令提示字元、Bash 或終端機工作階段,然後輸入下列命令:

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

az webapp log tail 命令有選項讓您使用 --provider 選項來篩選輸出。

若要使用 Azure CLI 將記錄檔下載為單一 ZIP 檔案,請開啟新的命令提示字元、Bash 或終端機工作階段,然後輸入下列命令:

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

執行此命令之前,您必須先在 Azure 入口網站或 Azure CLI 中啟用檔案系統記錄。

環境變數

在 Functions 中,應用程式設定 (例如服務連接字串) 在執行期間會公開為環境變數。 您可以使用 System.getenv("AzureWebJobsStorage") 來存取這些設定。

下列範例會使用名為 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);
    }
}

在 Java Functions 中使用相依性插入

Azure Functions Java 支援相依性插入 (DI) 軟體設計模式,這是在類別及其相依性之間達成控制反轉 (IoC) 的技術。 Java Azure Functions 提供勾點,以與 Functions Apps 中的熱門相依性插入架構整合。 Azure Functions Java SPI 包含 FunctionInstanceInjector 介面。 藉由實作這個介面,您可以傳回函式類別的執行個體,而且將會在此執行個體上叫用函式。 這可提供 SpringQuarkus、Google Guice、Dagger 等架構,讓您能夠建立函式執行個體並將其註冊到 IOC 容器。 這表示您可以使用這些相依性插入架構,自然地管理函式。

注意

Microsoft Azure Functions Java SPI 類型 (azure-function-java-spi) 是一個套件,其中包含所有 SPI 介面,可供第三方與 Microsoft Azure Functions 執行階段互動。

相依性插入的函式執行個體插入器

azure-function-java-spi 包含 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; 

} 

如需使用 FunctionInstanceInjector 與相依性插入架構整合的更多範例,請參閱存放庫。

下一步

如需 Azure Functions JAVA 開發的詳細資訊,請參閱下列資源: