本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
在 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 時,請務必指定 Java 類別,其中包含 Lambda 應在調用期間調用的主要處理常式方法。
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 函數 中使用基本和一般類型 (即 StringList、、 Integer或 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 記錄僅是 Java 17 執行期和更新版本的功能。在所有 Java 執行期中,可以使用類別來表示事件資料。在這種情況下,您可以使用 jackson
其他輸入事件類型
Java 中的 Lambda 函數有許多可能的輸入事件:
-
Integer、Long、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 的 AWS SDK v2
通常,您將使用 Lambda 函數與其他 AWS 資源互動或進行更新。與這些資源互動的最簡單方法是使用適用於 Java 的 AWS SDK v2。
注意
適用於 Java 的 AWS SDK (v1) 處於維護模式,將於 2025 年 12 月 31 日end-of-support。我們建議您未來僅使用適用於 Java v2 的 AWS 開發套件。
若要將 SDK 相依性新增至函數,請在 build.gradle Gradle 的 或 Maven 的 pom.xml檔案中新增這些相依性。我們建議您僅新增函數所需的程式庫。在先前的範例程式碼中,我們使用 software.amazon.awssdk.services.s3程式庫。在 Gradle 中,您可以在 的相依性區段中新增下列行來新增此相依性build.gradle:
implementation 'software.amazon.awssdk:s3:2.28.29'
在 Maven 中,在 的 <dependencies>區段中新增下列行pom.xml:
<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 的內部 APIs。這些內部 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 函數。