知識庫 / Spring / Spring Cloud RSS 訂閱

Spring Cloud Function for Azure 運行實例

Spring Cloud
HongKong
5
11:03 AM · Dec 06 ,2025

1. 概述

本教程將介紹如何使用 Spring Cloud Function (SCF) 框架,開發可在 Microsoft Azure Functions 上部署的 Java 應用程序。

我們將討論其關鍵概念,開發一個示例應用程序,將其部署到 Azure Functions 服務,並最終對其進行測試。

2. 關鍵概念

Azure Functions 服務提供了一個無服務器環境,我們可以在不擔心基礎設施管理的情況下部署我們的應用程序。我們可以通過遵循對應 SDK 庫定義的框架,使用不同的編程語言(如 Java、Python、C# 等)編寫應用程序。這些應用程序可以通過 Azure 服務發起的各種事件進行調用,例如 Blob 存儲、表存儲、Cosmos DB 數據庫、事件橋等。最終,應用程序可以處理事件數據並將其發送到目標系統。

Java Azure Function 庫提供了一個基於註解的強大編程模型。它有助於將方法註冊到事件中,接收來自源系統的數據,然後更新目標系統。

SCF 框架為 Azure Functions 和其他無服務器雲原生服務(如 AWS Lambda、Google Cloud FunctionsApache OpenWhisk)提供了一個抽象層。這得益於 SCF Azure Adapter

由於其統一的編程模型,它有助於代碼在不同平台上的一致性可移植性。 此外,我們可以輕鬆地將 Spring 框架的依賴注入等主要功能採用到無服務器應用程序中。

通常,我們實現核心功能接口,例如 Function<I, O>Consumer<I>Supplier<O>,並將其註冊為 Spring Bean。 然後,此 Bean 會自動注入到事件處理類中,其中端點方法使用 @FunctionName 註解應用。

此外,SCF 提供了一個 FunctionCatlog Bean,可以自動注入到事件處理類中。 通過使用 FunctionCatlaog#lookup(“<<bean name>>”) 方法,可以檢索已實現的泛型接口。 FunctionCatalog 類將其包裝在 SimpleFunctionRegistry.FunctionInvocationWrapper 類中,該類提供諸如函數組合和路由等附加功能。

我們將會在下一部分中進一步學習。

3. 先決條件

首先,我們需要一個活躍的 Azure 訂閲,才能部署 Azure Function 應用程序。

Java 應用程序的端點必須遵循 Azure Function 的編程模型,因此我們需要使用 Maven 依賴項

<dependency>
    <groupId>com.microsoft.azure.functions</groupId>
    <artifactId>azure-functions-java-library</artifactId>
    <version>3.1.0</version>
</dependency>

應用程序代碼準備就緒後,我們需要 Azure Functions Maven 插件,以便將其部署到 Azure。

<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-functions-maven-plugin</artifactId>
    <version>1.24.0</version>
</plugin>

Maven 工具幫助將應用程序打包成符合部署到 Azure Functions 服務的標準結構。 就像往常一樣,插件幫助指定 Azure Function 的 部署配置,例如 appnameresourcegroupappServicePlanName 等。

現在,讓我們定義 SCF 庫的 Maven 依賴項:Maven dependency

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-function-adapter-azure</artifactId>
    <version>4.1.3</version>
</dependency>

該庫使 Azure Function 處理程序(以 Java 編寫)能夠啓用 SCF(Service Fabric Component Model)和 Spring 依賴注入功能。 處理程序引用了我們應用 @FunctionName 註解的 Java 方法,並且也是從 Azure 服務(如 Blob 存儲、Cosmos DB Event Bridge 等)或自定義應用程序處理任何事件的入口點。

應用程序 JAR 的 Manifest 文件必須將入口點指向使用 @SpringBootApplication 註解的 Spring Boot 類。我們可以藉助 maven-jar-plugin 顯式設置它。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.4.2</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>com.baeldung.functions.AzureSpringCloudFunctionApplication</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

另一種方法是在 pom.xml文件中設置 start-class 屬性的值,但這種方法僅在我們將 spring-boot-starter-parent定義為父項目時有效:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.11</version>
    <relativePath/>
</parent>

最後,我們設置了 start-class 屬性:

<properties>
    <start-class>com.baeldung.functions.AzureSpringCloudFunctionApplication</start-class>
</properties>

該屬性確保 Spring Boot 主類被調用,從而初始化 Spring Bean 並允許它們自動注入到事件處理類中。

此外,Azure 期望應用程序具有特定的打包方式,因此我們需要禁用默認的 Spring Boot 打包並啓用 spring boot thin 佈局:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <dependencies>
        <dependency>
	    <groupId>org.springframework.boot.experimental</groupId>
	    <artifactId>spring-boot-thin-layout</artifactId>
	</dependency>
    </dependencies>
</plugin>

4. Java 實現

假設一個 Azure Function 應用根據員工的居住城市來計算員工的報 allowance。該應用通過 HTTP 接收一個員工的 JSON 字符串,並將其返回,同時在其中添加 allowance 到工資中。

4.1. 使用純 Spring Beans 的實現

首先,我們將定義用於開發此 Azure 應用程序的主要類:

讓我們從定義 員工薪資函數 開始:

public class EmployeeSalaryFunction implements Function<Employee, Employee> {

    @Override
    public Employee apply(Employee employee) {
        int allowance;
        switch (employee.getCity()) {
            case "Chicago" -> allowance = 5000;
            case "California" -> allowance = 2000;
            case "New York" -> allowance = 2500;
            default -> allowance = 1000;
        }
        int finalSalary = employee.getSalary() + allowance;
        employee.setSalary(finalSalary);
        return employee;
    }
}

EmployeeSalaryFunction 類實現了 java.util.function.Function 接口。EmployeeSalaryFunction#apply() 方法會將城市補貼添加到員工的基本工資中。

要將此類作為 Spring 託管 Bean 加載,將在ApplicationConfiguration 類中實例化它:

@Configuration
public class ApplicationConfiguration {
    @Bean
    public Function<Employee, Employee> employeeSalaryFunction() {
        return new EmployeeSalaryFunction();
    }
}

我們已將 @Configuration 註解應用於該類,讓 Spring 框架知道這是一個 Bean 定義的來源。 @Bean 方法 employeeSalaryFunction() 創建了 Spring Bean employeeSalaryFunction,類型為 EmployeeSalaryFunction

現在,讓我們使用 @Autowired 註解在 EmployeeSalaryHandler 類中注入這個 employeeSalaryFunction Bean:

@Component
public class EmployeeSalaryHandler {
    @Autowired
    private Function<Employee, Employee> employeeSalaryFunction;

    @FunctionName("employeeSalaryFunction")
    public HttpResponseMessage calculateSalary(
      @HttpTrigger(
        name="http",
        methods = HttpMethod.POST,
        authLevel = AuthorizationLevel.ANONYMOUS)HttpRequestMessage<Optional<Employee>> employeeHttpRequestMessage,
      ExecutionContext executionContext
    ) {
        Employee employeeRequest = employeeHttpRequestMessage.getBody().get();
        Employee employee = employeeSalaryFunction.apply(employeeRequest);
        return employeeHttpRequestMessage.createResponseBuilder(HttpStatus.OK)
          .body(employee)
          .build();
    }
}

Azure 事件處理函數主要遵循 Java Azure Function SDK 編程模型。但是,它利用了 Spring 框架的 註解在類級別以及 註解在 字段上的使用。 約定俗成地,確保自動注入的 Bean 的名稱與 註解中指定的名稱匹配,是一種良好的實踐。

同樣,我們可以擴展 Spring 框架對其他 Azure Function 觸發器的支持,例如 等。

4.2. 使用 SCF 實現

在必須動態檢索 Function bean 的場景下,顯式地自動注入所有 Function 並非最佳解決方案。

假設我們有多個實現方案,用於根據城市計算員工的最終薪資。

我們已定義了諸如 NewYorkSalaryCalculatorFnChicagoSalaryCalculatorFnCaliforniaSalaryCalculatorFn 等函數。這些函數根據員工的居住城市計算最終薪資。

以下是 CaliforniaSalaryCalculatorFn 類的詳細説明:

public class CaliforniaSalaryCalculatorFn implements Function<Employee, Employee> {
    @Override
    public Employee apply(Employee employee) {
        Integer finalSalary = employee.getSalary() + 3000;
        employee.setSalary(finalSalary);
        return employee;
    }
}

該方法為員工的基薪額外增加 3000 美元。其他城市員工的薪資計算函數大致相同。

入口方法 EmployeeSalaryHandler#calculateSalaryWithSCF() 使用 EmployeeSalaryFunctionWrapper#getCityBasedSalaryFunction() 以檢索用於計算員工薪資的特定城市函數:

public class EmployeeSalaryFunctionWrapper {
    private FunctionCatalog functionCatalog;

    public EmployeeSalaryFunctionWrapper(FunctionCatalog functionCatalog) {
        this.functionCatalog = functionCatalog;
    }

    public Function<Employee, Employee> getCityBasedSalaryFunction(Employee employee) {
        Function<Employee, Employee> salaryCalculatorFunction;
        switch (employee.getCity()) {
            case "Chicago" -> salaryCalculatorFunction = functionCatalog.lookup("chicagoSalaryCalculatorFn");
            case "California" -> salaryCalculatorFunction = functionCatalog.lookup("californiaSalaryCalculatorFn|defaultSalaryCalculatorFn");
            case "New York" -> salaryCalculatorFunction = functionCatalog.lookup("newYorkSalaryCalculatorFn");
            default -> salaryCalculatorFunction = functionCatalog.lookup("defaultSalaryCalculatorFn");
        }
        return salaryCalculatorFunction;
    }
}

我們可以通過將 EmployeeSalaryFunctionWrapper 對象傳遞給構造函數,來實例化它。然後,我們通過調用 EmployeeSalaryFunctionWrapper#getCityBasedSalaryFunction() 方法來獲取正確的薪資計算器 Bean。 FunctionCatalog#lookup(<<bean name>>) 方法用於檢索薪資計算器 Bean。

此外,該 Bean 是 SimpleFunctionRegistry$FunctionInvocationWrapper 的實例,支持函數組合和路由。例如,functionCatalog.lookup(“californiaSalaryCalculatorFn|defaultSalaryCalculatorFn”) 將返回一個組合後的函數。 此函數的 apply() 方法等效於:

californiaSalaryCalculatorFn.andThen(defaultSalaryCalculatorFn).apply(employee)

這意味着來自加州的員工將同時獲得州税和額外的默認津貼。

最後,讓我們來看一下事件處理函數:

@Component
public class EmployeeSalaryHandler {
    @Autowired
    private FunctionCatalog functionCatalog;

    @FunctionName("calculateSalaryWithSCF")
    public HttpResponseMessage calculateSalaryWithSCF(
      @HttpTrigger(
        name="http",
        methods = HttpMethod.POST,
        authLevel = AuthorizationLevel.ANONYMOUS)HttpRequestMessage<Optional<Employee>> employeeHttpRequestMessage,
      ExecutionContext executionContext
    ) {
        Employee employeeRequest = employeeHttpRequestMessage.getBody().get();
        executionContext.getLogger().info("Salary of " + employeeRequest.getName() + " is:" + employeeRequest.getSalary());
        EmployeeSalaryFunctionWrapper employeeSalaryFunctionWrapper = new EmployeeSalaryFunctionWrapper(functionCatalog);
        Function<Employee, Employee> cityBasedSalaryFunction = employeeSalaryFunctionWrapper.getCityBasedSalaryFunction(employeeRequest);
        Employee employee = cityBasedSalaryFunction.apply(employeeRequest);

        executionContext.getLogger().info("Final salary of " + employee.getName() + " is:" + employee.getSalary());
        return employeeHttpRequestMessage.createResponseBuilder(HttpStatus.OK)
          .body(employee)
          .build();
    }
}

與上一節中討論的 calcuateSalary() 方法不同,calculateSalaryWithSCF() 方法使用 FunctionCatalog 對象,該對象自動注入到類中。

5. 部署和運行應用程序

我們將使用 Maven 來編譯、打包和在 Azure Functions 上部署應用程序。 讓我們從 IntelliJ 中運行 Maven 目標:

部署成功後,函數將在 Azure 門户中顯示:

最後,在從 Azure 門户獲取端點之後,我們可以調用它們並檢查結果:

此外,函數調用可以在 Azure 門户中確認:

6. 結論

在本文中,我們學習瞭如何使用 Spring Cloud Function 框架開發 Java Azure Function 應用程序。該框架允許使用基本的 Spring 依賴注入功能。此外,<em >FunctionCatalog</em> 類提供了關於函數(如組合和路由)的特性。

雖然該框架在某些情況下可能比低級別的 Java Azure Function 庫增加一些開銷,但它提供了顯著的設計優勢。因此,應該在仔細評估應用程序的性能需求之後再採用。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.