在 Java 中定義 Lambda 函數處理常式
Lambda 函數處理常式是您的函數程式碼中處理事件的方法。當有人呼叫您的函數時,Lambda 會執行處理常式方法。函數會執行,直到處理常式傳回回應、結束或逾時為止。
本頁介紹了如何在 Java 中使用 Lambda 函式處理常式,包括專案設定選項、命名慣例及最佳實務。本頁還提供了一個 Java Lambda 函式範例,該函式會取得訂單的相關資訊、產生文字檔案收據,並將該檔案放入 Amazon Simple Storage Service (Amazon S3) 儲存貯體。如需編寫函數後如何部署函數的詳細資訊,請參閱使用 .zip 或 JAR 封存檔部署 Java Lambda 函數或使用容器映像部署 Java Lambda 函數。
章節
設定 Java 處理常式專案
使用 Java 開發 Lambda 函式時,流程涉及編寫程式碼、編譯程式碼,以及將編譯後的成品部署至 Lambda。可以透過各種方式初始化 Java Lambda 專案。例如,您可以使用下列工具:適用於 Lambda 函式的 Maven Archetype
一個典型的 Java Lambda 函式專案遵循以下一般結構:
/project-root └ src └ main └ java └ example └ OrderHandler.java (contains main handler) └ <other_supporting_classes> └ build.gradle OR pom.xml
可以使用 Maven 或 Gradle 建置專案並管理相依項。
函式的主要處理常式邏輯位於 src/main/java/example 目錄下的 Java 檔案中。在此頁面上的範例中,我們將該檔案命名為 OrderHandler.java。除了該檔案外,您亦可視需要納入其他 Java 類別。將函式部署至 Lambda 時,請務必指定包含 Lambda 在調用期間應調用的主要處理常式方法的 Java 類別。
Java Lambda 函式程式碼範例
以下 Java 21 Lambda 函式程式碼範例會取得訂單的相關資訊、產生文字檔案收據,並將該檔案放入 Amazon S3 儲存貯體。
範例 OrderHandler.java Lambda 函數
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import java.nio.charset.StandardCharsets; /** * Lambda handler for processing orders and storing receipts in S3. */ public class OrderHandler implements RequestHandler<OrderHandler.Order, String> { private static final S3Client S3_CLIENT = S3Client.builder().build(); /** * Record to model the input event. */ public record Order(String orderId, double amount, String item) {} @Override public String handleRequest(Order event, Context context) { try { // Access environment variables String bucketName = System.getenv("RECEIPT_BUCKET"); if (bucketName == null || bucketName.isEmpty()) { throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set"); } // Create the receipt content and key destination String receiptContent = String.format("OrderID: %s\nAmount: $%.2f\nItem: %s", event.orderId(), event.amount(), event.item()); String key = "receipts/" + event.orderId() + ".txt"; // Upload the receipt to S3 uploadReceiptToS3(bucketName, key, receiptContent); context.getLogger().log("Successfully processed order " + event.orderId() + " and stored receipt in S3 bucket " + bucketName); return "Success"; } catch (Exception e) { context.getLogger().log("Failed to process order: " + e.getMessage()); throw new RuntimeException(e); } } private void uploadReceiptToS3(String bucketName, String key, String receiptContent) { try { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); // Convert the receipt content to bytes and upload to S3 S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8))); } catch (S3Exception e) { throw new RuntimeException("Failed to upload receipt to S3: " + e.awsErrorDetails().errorMessage(), e); } } }
此 OrderHandler.java 檔案包含以下程式碼區段:
-
package example:在 Java 中,此套件名稱可自訂,但必須與專案的目錄結構相符。本處採用package example,原因在於目錄結構為src/main/java/example。 -
import陳述式:用於匯入 Lambda 函式所需的 Java 類別。 -
public class OrderHandler ...:此程式碼會定義您的 Java 類別,且必須為有效的類別定義。 -
private static final S3Client S3_CLIENT ...:此程式碼在類別的所有方法外部初始化 S3 用戶端。這會使 Lambda 在初始化階段執行此程式碼。 -
public record Order ...:在此自訂 Java 記錄中定義預期輸入事件的結構。 -
public String handleRequest(Order event, Context context):這是主要處理常式方法,其中包含應用程式的主要邏輯。 -
private void uploadReceiptToS3(...) {}:這是主要handleRequest處理常式方法所參考的協助程式方法。
此函式附帶下列 build.gradle 或 pom.xml 檔案。
若要讓此函式正常運作,其執行角色必須允許 s3:PutObject 動作。此外,請確保定義 RECEIPT_BUCKET 環境變數。成功調用後,Amazon S3 儲存貯體應包含收據檔案。
注意
此函式可能需要額外的組態設定,才能順利執行而不發生逾時。建議設定 256 MB 記憶體以及 10 秒逾時。首次調用可能因冷啟動而耗費更長時間。後續調用因執行環境可重複使用,執行速度會大幅提升。
Java 處理常式的有效類別定義
為了定義類別,aws-lambda-java-core
RequestHandler 介面是一個一般類型,它有兩個參數:輸入類型和輸出類型。兩種類型都必須是物件。在此範例中,OrderHandler 類別會實作 RequestHandler<OrderHandler.Order, String>。輸入類型是我們在類別中定義的 Order 記錄,輸出類型是 String。
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> { ... }
當您使用此介面時,Java 執行時期會將事件反序列化為具有輸入類型的物件,並將輸出序列化為文字。當內建序列化與您的輸入和輸出類型一同作業時,請使用此介面。
若要使用自訂序列化,您可以實作 RequestStreamHandler 介面。透過此介面,Lambda 會將處理常式傳遞給一個輸入串流和輸出串流。處理常式會從輸入串流讀取位元組,寫入到輸出串流,並傳回 void 值。如需使用 Java 21 執行時期的實作範例,請參閱 HandlerStream.java
若您的 Java 函式僅使用基本類型與泛型類型 (即 String、Integer、List 或 Map),則無需實作介面。例如,若函式接收一個 Map<String, String> 輸入並傳回 String,則類別定義和處理常式簽章可能如下所示:
public class ExampleHandler { public String handleRequest(Map<String, String> input, Context context) { ... } }
此外,如果未實作介面,則內容物件是選用項目。例如,類別定義和處理常式簽章可能如下所示:
public class NoContextHandler { public String handleRequest(Map<String, String> input) { ... } }
處理常式命名慣例
對於 Java 中的 Lambda 函式,如果您實作 RequestHandler 或 RequestStreamHandler 介面,則主要處理常式方法必須命名為 handleRequest。此外,請在 handleRequest 方法上方加上 @Override 標籤。將函式部署到 Lambda 時,請以下列格式在函式的組態中指定主要處理常式:
-
<package>.<Class>– 例如example.OrderHandler。
對於 Java 中未實作 RequestHandler 或 RequestStreamHandler 介面的 Lambda 函式,您可以將處理常式命名為任何名稱。將函式部署到 Lambda 時,請以下列格式在函式的組態中指定主要處理常式:
-
<package>.<Class>::<handler_method_name>– 例如example.Handler::mainHandler。
定義和存取輸入事件物件
JSON 是 Lambda 函數最常見的標準輸入格式。在此範例中,函數預期輸入類似以下內容:
{ "orderId": "12345", "amount": 199.99, "item": "Wireless Headphones" }
使用 Java 17 或更新版本開發 Lambda 函式時,可將預期輸入事件的結構定義為 Java 記錄。在此範例中,我們定義了 OrderHandler 類別中的一條記錄來代表 Order 物件:
public record Order(String orderId, double amount, String item) {}
該記錄符合預期的輸入結構。定義記錄之後,您即可編寫處理常式簽章,其會接收符合該記錄定義的 JSON 輸入。Java 執行時期會自動將此 JSON 反序列化為 Java 物件。然後,您可以存取該物件的欄位。例如,使用 orderId 可以從原始輸入擷取 event.orderId 的值。
注意
僅 Java 17 及更新版本的執行時期支援 Java 記錄功能。在所有 Java 執行期中,可以使用類別來表示事件資料。在此類案例中,您可以使用 jackson
其他輸入事件類型
Java 中的 Lambda 函式支援多種可能的輸入事件:
-
IntegerLong、Double、等等 - 事件是沒有其他格式的數字,例如3.5。Java 執行時期會將值轉換為指定類型的物件。 -
String- 事件是 JSON 字串,包括引號,例如 。“My string”執行時期會將值轉換為不含引號的String物件。 -
List<Integer>List<String>、List<Object>、等等 - 該事件是一個 JSON 陣列。執行階段會將它還原序列化為指定類型或介面的物件。 -
InputStream- 該事件是任何 JSON 類型。執行階段會將文件的位元組串流傳遞給處理常式而不進行修改。您還原序列化輸入和並將輸出寫入輸出串流。 -
程式庫類型 – 對於由其他 AWS 服務傳送的事件,請使用 aws-lambda-java-events
程式庫中的類型。例如,若 Amazon Simple Queue Service (SQS) 調用您的 Lambda 函式,則以 SQSEvent物件作為輸入。
存取和使用 Lambda 內容物件
Lambda 內容物件 包含有關調用、函數以及執行環境的資訊。在此範例中,內容物件的類型為 com.amazonaws.services.lambda.runtime.Context,並且內容物件也是主要處理常式函式的第二個引數。
public String handleRequest(Order event, Context context) { ... }
如果您的類別實作了 RequestHandler
如果您使用 AWS SDK 呼叫其他服務,則有幾個關鍵區域需要內容物件。例如,若要為 Amazon CloudWatch 生成函式日誌,您可以使用 context.getLogger() 方法取得用於記錄的 LambdaLogger 物件。在此範例中,如果處理因任何原因失敗,我們可以使用記錄器來記錄錯誤訊息:
context.getLogger().log("Failed to process order: " + e.getMessage());
除了記錄日誌之外,您還可以將內容物件用於函式監控。如需內容物件的詳細資訊,請參閱使用 Lambda 內容物件擷取 Java 函數資訊。
在處理常式中使用適用於 Java v2 的 AWS SDK
通常,您會使用 Lambda 函式與其他 AWS 資源互動或對其進行更新。與這些資源互動的最簡單方法便是使用適用於 Java v2 的 AWS SDK。
注意
適用於 Java (v1) 的 AWS SDK 目前處於維護模式,將於 2025 年 12 月 31 日停止支援。建議今後僅使用適用於 Java v2 的 AWS SDK。
若要將 SDK 相依項新增至函式,請在適用於 Gradle 的 build.gradle 檔案或適用於 Maven 的 pom.xml 檔案中納入這些相依項。建議僅新增函式所需的程式庫。在先前的範例程式碼中,我們使用了 software.amazon.awssdk.services.s3 程式庫。在 Gradle 中,您可以透過在 build.gradle 的相依項區段中新增以下一行程式碼來新增此相依項:
implementation 'software.amazon.awssdk:s3:2.28.29'
在 Maven 中,在 pom.xml 的 <dependencies> 區段中新增以下程式碼行:
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.28.29</version> </dependency>
注意
SDK 可能不是最新版本。請為應用程式選擇適當的 SDK 版本。
然後,直接在 Java 類別中匯入相依項:
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception;
隨後,範例程式碼會初始化 Amazon S3 用戶端,如下所示:
private static final S3Client S3_CLIENT = S3Client.builder().build();
在此範例中,我們在主要處理常式函式外部初始化 Amazon S3 用戶端,以免每次調用函式時都必須初始化該用戶端。初始化 SDK 用戶端之後,您即可用其與其他 AWS 服務互動。此範例程式碼會呼叫 Amazon S3 PutObject API,如下所示:
PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); // Convert the receipt content to bytes and upload to S3 S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
存取環境變數
在處理常式程式碼中,您可以使用 System.getenv() 方法來引用任何環境變數。在此範例中,我們使用以下程式碼來參考定義的 RECEIPT_BUCKET 環境變數:
String bucketName = System.getenv("RECEIPT_BUCKET"); if (bucketName == null || bucketName.isEmpty()) { throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set"); }
使用全域狀態
在首次調用函數之前,Lambda 會在初始化階段執行靜態程式碼和類別建構函數。在初始化期間建立的資源會在調用間隔期間保留在記憶體中,無需您在每次調用函式時都建立這些資源。
在範例程式碼中,S3 用戶端初始化程式碼位於主要處理常式方法外部。執行時期會在函式處理第一個事件之前初始化用戶端,且用戶端在所有調用中均可供重複使用。
Java Lambda 函數的程式碼最佳實務
請遵循下列清單中的準則,在建置 Lambda 函數時使用最佳編碼實務:
-
區隔 Lambda 處理常式與您的核心邏輯。能允許您製作更多可測單位的函式。
-
控制函數部署套件內的相依性。AWS Lambda 執行環境包含多個程式庫。若要啟用最新的一組功能與安全更新,Lambda 會定期更新這些程式庫。這些更新可能會為您的 Lambda 函數行為帶來細微的變更。若要完全掌控您函式所使用的相依性,請利用部署套件封裝您的所有相依性。
-
最小化依存項目的複雜性。偏好更簡易的框架,其可快速在執行環境啟動時載入。例如,偏好更簡易的 Java 相依性置入 (IoC) 架構如 Dagger
或 Guice ,勝於複雜的架構如 Spring Framework 。 -
將部署套件最小化至執行時期所必要的套件大小。這能減少您的部署套件被下載與呼叫前解壓縮的時間。對於以 Java 撰寫的函數,請避免上傳整個 AWS SDK 程式庫做為部署套件的一部分。或者,選擇性倚賴取得您需要的軟體開發套件元件的模組 (例如 DynamoDB、Amazon S3 開發套件模組,以及 Lambda 核心程式庫
)。
請利用執行環境重新使用來改看函式的效能。在函式處理常式之外初始化 SDK 用戶端和資料庫連線,並在本機快取 /tmp 目錄中的靜態資產。由您函式的相同執行個體處理的後續叫用可以重複使用這些資源。這可藉由減少函數執行時間來節省成本。
若要避免叫用間洩漏潛在資料,請不要使用執行環境來儲存使用者資料、事件,或其他牽涉安全性的資訊。如果您的函式依賴無法存放在處理常式內記憶體中的可變狀態,請考慮為每個使用者建立個別函式或個別函式版本。
使用 Keep-Alive 指令維持持續連線的狀態。Lambda 會隨著時間的推移清除閒置連線。叫用函數時嘗試重複使用閒置連線將導致連線錯誤。若要維護持續連線,請使用與執行階段相關聯的 keep-alive (保持啟用) 指令。如需範例,請參閱在 Node.js 中重複使用 Keep-Alive 的連線。
使用環境變數將操作參數傳遞給您的函數。例如,如果您正在寫入到 Amazon S3 儲存貯體,而非對您正在寫入的儲存貯體名稱進行硬式編碼,請將儲存貯體名稱設定為環境變數。
避免在 Lambda 函數中使用遞迴調用,其中函數會調用自己或啟動可能再次調用函數的程序。這會導致意外的函式呼叫量與升高的成本。若您看到意外的調用數量,當更新程式碼時,請立刻將函數的預留並行設為 0,以調節對函數的所有調用。
請勿在您的 Lambda 函數程式碼中使用未記錄的非公有 API。對於 AWS Lambda 受管執行時間,Lambda 會定期將安全性和函數更新套用至 Lambda 的內部 API。這些內部 API 更新可能是向後不相容的,這會導致意外結果,例如若您的函數依賴於這些非公有 API,則叫用失敗。請參閱 API 參考查看公開可用 API 的清單。
撰寫等冪程式碼。為函數撰寫等冪程式碼可確保採用相同方式來處理重複事件。程式碼應正確驗證事件並正常處理重複的事件。如需詳細資訊,請參閱 How do I make my Lambda function idempotent?
-
避免使用 Java DNS 快取。Lambda 函數已快取 DNS 回應。如果您使用其他 DNS 快取,則可能會發生連線逾時。
java.util.logging.Logger類別可以間接啟用 JVM DNS 快取。若要覆寫預設設定,請先將 networkaddress.cache.ttl設定為 0,再初始化 logger。範例:public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); } -
將相依性
.jar檔案置於不同的 /lib 目錄,縮短 Lambda 解壓縮以 Java 撰寫之部署套件的時間。這比將您所有函式程式碼全部放入具大量.class檔案的單一 jar 更快速。如需說明,請參閱 使用 .zip 或 JAR 封存檔部署 Java Lambda 函數。