选择您的 Cookie 首选项

我们使用必要 Cookie 和类似工具提供我们的网站和服务。我们使用性能 Cookie 收集匿名统计数据,以便我们可以了解客户如何使用我们的网站并进行改进。必要 Cookie 无法停用,但您可以单击“自定义”或“拒绝”来拒绝性能 Cookie。

如果您同意,AWS 和经批准的第三方还将使用 Cookie 提供有用的网站功能、记住您的首选项并显示相关内容,包括相关广告。要接受或拒绝所有非必要 Cookie,请单击“接受”或“拒绝”。要做出更详细的选择,请单击“自定义”。

定义采用 Java 的 Lambda 函数处理程序

聚焦模式
定义采用 Java 的 Lambda 函数处理程序 - AWS Lambda

Lambda 函数处理程序是函数代码中处理事件的方法。当调用函数时,Lambda 运行处理程序方法。您的函数会一直运行,直到处理程序返回响应、退出或超时。

本页介绍如何使用 Java Lambda 函数处理程序,包括项目设置选项、命名约定和最佳实践。本页还包括 Java Lambda 函数的示例,该函数接收订单信息,生成文本文件收据,然后将此文件放入 Amazon Simple Storage Service(S3)存储桶中。有关如何在编写函数后部署函数的信息,请参阅使用 .zip 或 JAR 文件归档部署 Java Lambda 函数使用容器镜像部署 Java Lambda 函数

设置 Java 处理程序项目

使用 Java Lambda 函数时,该过程涉及编写代码、对其进行编译,以及将编译后的构件部署到 Lambda。您可以通过多种方式初始化 Java Lambda 项目。例如,您可以使用 Lambda 函数的 Maven Archetype 等工具、AWS SAM CLI sam init 命令,甚至可以在您首选的 IDE(如 IntelliJ IDEA 或 Visual Studio Code)中使用标准 Java 项目设置。或者,您也可以手动创建所需的文件结构。

典型的 Java Lambda 函数项目遵循以下一般结构:

/project-root └ srcmain └ 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.gradlepom.xml 文件。

build.gradle
plugins { id 'java' } repositories { mavenCentral() } dependencies { implementation 'com.amazonaws:aws-lambda-java-core:1.2.3' implementation 'software.amazon.awssdk:s3:2.28.29' implementation 'org.slf4j:slf4j-nop:2.0.16' } task buildZip(type: Zip) { from compileJava from processResources into('lib') { from configurations.runtimeClasspath } } java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } build.dependsOn buildZip
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>example-java</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>example-java-function</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.28.29</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>2.0.16</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.5.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*</exclude> <exclude>META-INF/versions/**</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <release>21</release> </configuration> </plugin> </plugins> </build> </project>

示例 build.gradle 和 pom.xml 文件

此函数随附以下 build.gradlepom.xml 文件。

build.gradle
plugins { id 'java' } repositories { mavenCentral() } dependencies { implementation 'com.amazonaws:aws-lambda-java-core:1.2.3' implementation 'software.amazon.awssdk:s3:2.28.29' implementation 'org.slf4j:slf4j-nop:2.0.16' } task buildZip(type: Zip) { from compileJava from processResources into('lib') { from configurations.runtimeClasspath } } java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } build.dependsOn buildZip
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>example-java</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>example-java-function</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.28.29</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>2.0.16</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.5.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*</exclude> <exclude>META-INF/versions/**</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <release>21</release> </configuration> </plugin> </plugins> </build> </project>
plugins { id 'java' } repositories { mavenCentral() } dependencies { implementation 'com.amazonaws:aws-lambda-java-core:1.2.3' implementation 'software.amazon.awssdk:s3:2.28.29' implementation 'org.slf4j:slf4j-nop:2.0.16' } task buildZip(type: Zip) { from compileJava from processResources into('lib') { from configurations.runtimeClasspath } } java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } build.dependsOn buildZip

要使此函数正常运行,其执行角色必须允许 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 函数中只使用基本类型和泛型类型(即 StringInteger=ListMap),则无需实现接口。例如,如果您的函数接受 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 函数,如果您要实现 RequestHandlerRequestStreamHandler 接口,则主处理程序方法必须命名为 handleRequest。此外,请在 handleRequest 方法上方添加 @Override 标签。将函数部署到 Lambda 时,请按以下格式在函数配置中指定主处理程序:

  • <package>.<Class>:例如,example.OrderHandler

对于未实现 RequestHandlerRequestStreamHandler 接口的 Java Lambda 函数,您可以使用任何处理程序名称。将函数部署到 Lambda 时,请按以下格式在函数配置中指定主处理程序:

  • <package>.<Class>::<handler_method_name>:例如,example.Handler::mainHandler

定义和访问输入事件对象

JSON 是 Lambda 函数最常用且最标准的输入格式。在此示例中,该函数需要类似于下方的输入:

{ "order_id": "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 对象。然后,您可以访问该对象的字段。例如,event.orderId 从原始输入中检索 orderId 的值。

注意

Java 记录是 Java 17 运行时及更高版本独有的一项功能。在所有 Java 运行时系统中,您可以使用类来表示事件数据。在这种情况下,您可以使用如 jackson 之类的库来反序列化 JSON 输入。

其他输入事件类型

Java Lambda 函数有许多可能的输入事件:

  • Integer LongDouble、等 – 事件是一个没有其他格式的数字(例如,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) { ... }

如果您的类实现了 RequestHandlerRequestStreamHandler 接口,则上下文对象是必需参数。否则,上下文对象是可选参数。有关有效接受处理程序签名的更多信息,请参阅Java 处理程序的有效类定义

如果您使用 AWS SDK 调用其他服务,则多个关键区域中均需要上下文对象。例如,要为 Amazon CloudWatch 生成函数日志,您可以使用 context.getLogger() 方法获取用于日志记录的 LambdaLogger 对象。在此示例中,如果由于任何原因处理失败,我们可以使用记录器记录错误消息:

context.getLogger().log("Failed to process order: " + e.getMessage());

除了日志记录之外,您还可以使用上下文对象进行函数监控。有关上下文对象的更多信息,请参阅使用 Lambda 上下文对象检索 Java 函数信息

在处理程序中使用 AWS SDK for Java v2

通常,您将使用 Lambda 函数与其他 AWS 资源进行交互或对其进行更新。与此类资源最简单的交互方法是使用 AWS SDK for Java v2。

注意

AWS SDK for Java(v1)处于维护模式,其支持的终止日期为 2025 年 12 月 31 日。建议您今后仅使用 AWS SDK for Java v2。

要向函数添加 SDK 依赖项,请将其添加到 build.gradle(Gradle)或 pom.xml 文件(Maven)。建议仅添加函数所需的库。在上述示例代码中,我们使用了 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 客户端初始化代码位于主处理程序方法之外。运行时会在函数处理其第一个事件之前初始化客户端,这可能会导致更长的处理时间。后续事件的处理速度则要快得多,因为 Lambda 不需要再次初始化客户端。

Java Lambda 函数的代码最佳实践

在构建 Lambda 函数时,请遵循以下列表中的指南,采用最佳编码实践:

  • 从核心逻辑中分离 Lambda 处理程序。这样您可以创建更容易进行单元测试的函数。

  • 控制函数部署程序包中的依赖关系。AWS Lambda 执行环境包含许多库。Lambda 会定期更新这些库,以支持最新的功能组合和安全更新。这些更新可能会使 Lambda 函数的行为发生细微变化。要完全控制您的函数所用的依赖项,请使用部署程序包来打包所有依赖项。

  • 将依赖关系的复杂性降至最低。首选在执行环境启动时可以快速加载的更简单的框架。例如,首选更简单的 Java 依赖关系注入 (IoC) 框架,如 DaggerGuice,而不是更复杂的 Spring Framework

  • 将部署程序包大小精简为只包含运行时必要的部分。这样会减少调用前下载和解压缩部署程序包所需的时间。对于用 Java 编写的函数,请勿将整个 AWS SDK 库作为部署包的一部分上传,而是要根据所需的模块有选择地挑选软件开发工具包中的组件(例如 DynamoDB、Simple Storage Service (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 列表。

  • 编写幂等代码。为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息,请参阅如何使我的 Lambda 函数具有幂等性?

  • 避免使用 Java DNS 缓存。Lambda 函数已经缓存了 DNS 响应。如果您使用了另一个 DNS 缓存,则可能会出现连接超时。

    java.util.logging.Logger 类可以间接启用 JVM DNS 缓存。要覆盖默认设置,请在初始化 logger 之前将 networkaddress.cache.ttl 设置为 0。示例:

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

隐私网站条款Cookie 首选项
© 2025, Amazon Web Services, Inc. 或其附属公司。保留所有权利。