

# 使用 Java 构建 Lambda 函数
<a name="lambda-java"></a>

您可以在 AWS Lambda 中运行 Java 代码。Lambda 为 Java 提供[运行时](lambda-runtimes.md)，运行您的代码来处理事件。您的代码在 Amazon Linux 环境中运行，该环境包含来自您所管理的 AWS Identity and Access Management (IAM) 的角色的 AWS 凭证。

Lambda 支持以下 Java 运行时。<a name="java-runtimes"></a>


| 名称 | 标识符 | 操作系统 | 弃用日期 | 阻止函数创建 | 阻止函数更新 | 
| --- | --- | --- | --- | --- | --- | 
|  Java 25  |  `java25`  |  Amazon Linux 2023  |   2029 年 6 月 30 日   |   2029 年 7 月 31 日   |   2029 年 8 月 31 日   | 
|  Java 21  |  `java21`  |  Amazon Linux 2023  |   2029 年 6 月 30 日   |   2029 年 7 月 31 日   |   2029 年 8 月 31 日   | 
|  Java 17  |  `java17`  |  Amazon Linux 2  |   2027 年 6 月 30 日   |   2027 年 7 月 31 日   |   2027 年 8 月 31 日   | 
|  Java 11  |  `java11`  |  Amazon Linux 2  |   2027 年 6 月 30 日   |   2027 年 7 月 31 日   |   2027 年 8 月 31 日   | 
|  Java 8  |  `java8.al2`  |  Amazon Linux 2  |   2027 年 6 月 30 日   |   2027 年 7 月 31 日   |   2027 年 8 月 31 日   | 

AWS 提供以下适用于 Java 函数的库。这些库可通过 [Maven 中央存储库](https://search.maven.org/search?q=g:com.amazonaws)获得。
+ [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-core)（必需）– 定义处理程序方法接口和运行时传递给处理程序的上下文对象。如果您定义自己的输入类型，则这是您唯一需要的库。
+ [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events) – 调用 Lambda 函数服务的事件的输入类型。
+ [com.amazonaws:aws-lambda-java-log4j2](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-log4j2) – Apache Log4j 2 的 Appender 库，可用于将当前调用的请求 ID 添加到[函数日志](java-logging.md)中。
+ [适用于 Java 2.0 的AWS开发工具包](https://github.com/aws/aws-sdk-java-v2)：适用于 Java 编程语言的官方AWS开发工具包。

将这些库添加到您的构建定义中，如下所示：

------
#### [ Gradle ]

```
dependencies {
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
    implementation 'com.amazonaws:aws-lambda-java-events:3.11.1'
    runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1'
}
```

------
#### [ Maven ]

```
  <dependencies>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-core</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-events</artifactId>
      <version>3.11.1</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-log4j2</artifactId>
      <version>1.5.1</version>
    </dependency>
  </dependencies>
```

------

**重要**  
请勿使用 JDK API 的私有组件，例如私有字段、方法或类。非公共 API 组件可能会在任何更新中更改或删除，从而导致您的应用程序中断。

**创建 Java 函数**

1. 打开 [Lambda 控制台](https://console.aws.amazon.com/lambda)。

1. 选择 **Create function**（创建函数）。

1. 配置以下设置：
   + **函数名称**：输入函数名称。
   + **运行时系统**：选择 **Java 25**。

1. 选择**创建函数**。

控制台会创建具有名为 `Hello` 的处理程序类的 Lambda 函数。由于 Java 是编译语言，因此您无法在 Lambda 控制台中查看或编辑源代码，但可以修改源代码的配置、调用源代码以及配置触发器。

**注意**  
要在本地环境中开始应用程序开发，请部署本指南的 GitHub 存储库中提供的其中一个[示例应用程序](java-samples.md)。

`Hello` 类具有一个名为 `handleRequest` 的函数，此函数接受事件对象和上下文对象。这是 Lambda 在调用函数时调用的[处理函数](java-handler.md)。Java 函数运行时从 Lambda 获取调用事件并将其传递到处理程序。在函数配置中，处理程序值为 `example.Hello::handleRequest`。

要更新函数的代码，您需要创建一个部署包，这是一个包含函数代码的 .zip 文件归档。随着函数开发的进行，您需要将函数代码存储在源代码控制中、添加库和实现部署自动化。首先，通过命令行[创建部署包](java-package.md)并更新代码。

除了调用事件之外，函数运行时还将上下文对象传递给处理程序。[上下文对象](java-context.md)包含有关调用、函数和执行环境的其他信息。环境变量中提供了更多信息。

您的 Lambda 函数附带了 CloudWatch Logs 日志组。函数运行时会将每次调用的详细信息发送到 CloudWatch Logs。该运行时会中继调用期间[函数输出的任何日志](java-logging.md)。如果您的函数返回错误，则 Lambda 将为错误设置格式，并将其返回给调用方。

**Topics**
+ [定义采用 Java 的 Lambda 函数处理程序](java-handler.md)
+ [使用 .zip 或 JAR 文件归档部署 Java Lambda 函数](java-package.md)
+ [使用容器镜像部署 Java Lambda 函数](java-image.md)
+ [使用 Java Lambda 函数的层](java-layers.md)
+ [自定义 Lambda Java 函数的序列化](java-custom-serialization.md)
+ [自定义 Lambda 函数的 Java 运行时启动行为](java-customization.md)
+ [使用 Lambda 上下文对象检索 Java 函数信息](java-context.md)
+ [Java Lambda 函数日志记录和监控](java-logging.md)
+ [在 AWS Lambda 中检测 Java 代码](java-tracing.md)
+ [AWS Lambda 的 Java 示例应用程序](java-samples.md)

# 定义采用 Java 的 Lambda 函数处理程序
<a name="java-handler"></a>

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

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

**Topics**
+ [设置 Java 处理程序项目](#java-handler-setup)
+ [示例 Java Lambda 函数代码](#java-example-code)
+ [Java 处理程序的有效类定义](#java-handler-signatures)
+ [处理程序命名约定](#java-example-naming)
+ [定义和访问输入事件对象](#java-handler-input)
+ [访问和使用 Lambda 上下文对象](#java-example-context)
+ [在处理程序中使用 AWS SDK for Java v2](#java-example-sdk-usage)
+ [评估环境变量](#java-example-envvars)
+ [使用全局状态](#java-handler-state)
+ [Java Lambda 函数的代码最佳实践](#java-best-practices)

## 设置 Java 处理程序项目
<a name="java-handler-setup"></a>

使用 Java Lambda 函数时，该过程涉及编写代码、对其进行编译，以及将编译后的构件部署到 Lambda。您可以通过多种方式初始化 Java Lambda 项目。例如，您可以使用 [Lambda 函数的 Maven Archetype](https://github.com/aws/aws-sdk-java-v2/tree/master/archetypes/archetype-lambda) 等工具、AWS SAM CLI [sam init 命令](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-init.html)，甚至可以在您首选的 IDE（如 IntelliJ IDEA 或 Visual Studio Code）中使用标准 Java 项目设置。或者，您也可以手动创建所需的文件结构。

典型的 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 函数代码
<a name="java-example-code"></a>

以下示例 Java 21 Lambda 函数代码接收有关订单的信息，生成文本文件收据，并将此文件放入 Amazon S3 存储桶中。

**Example `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 类，并且必须是[有效的类定义](#java-handler-signatures)。
+ `private static final S3Client S3_CLIENT ...`：此部分在类的任何方法外部初始化 S3 客户端。这会导致 Lambda 在[初始化阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib)运行此代码。
+ `public record Order ...`：在此自定义 Java [记录](https://openjdk.org/jeps/395)中定义预期输入事件的形状。
+ `public String handleRequest(Order event, Context context)`：这是包含主应用程序逻辑的**主处理程序方法**。
+ `private void uploadReceiptToS3(...) {}`：这是主 `handleRequest` 处理程序方法引用的帮助程序方法。

### 示例 build.gradle 和 pom.xml 文件
<a name="java-gradle-maven-example"></a>

此函数随附以下 `build.gradle` 或 `pom.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>
```

------

要使此函数正常运行，其[执行角色](lambda-intro-execution-role.md)必须允许 `s3:PutObject` 操作。此外，请确保您定义了 `RECEIPT_BUCKET` 环境变量。成功调用后，Amazon S3 存储桶应包含接收文件。

**注意**  
此函数可能需要额外的配置设置才能成功运行而不会超时。建议配置 256 MB 的内存和 10 秒的超时时间。由于是[冷启动](lambda-runtime-environment.md#cold-start-latency)，第一次调用可能需要额外的时间。由于重复使用执行环境，后续调用的运行速度应该会快得多。

## Java 处理程序的有效类定义
<a name="java-handler-signatures"></a>

为了定义您的类，[aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-core) 库为处理程序方法定义了两个接口。使用提供的接口简化处理程序配置，并在编译时验证方法签名。
+ [ com.amazonaws.services.lambda.runtime.RequestHandler](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestHandler.java)
+ [ com.amazonaws.services.lambda.runtime.RequestStreamHandler](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestStreamHandler.java)

`RequestHandler` 接口是一个泛型类型，它采用两个参数：输入类型和输出类型。这两种类型都必须是对象。在此示例中，我们的 `OrderHandler` 类实现 `RequestHandler<OrderHandler.Order, String>`。输入类型是我们在类中定义的 `Order` 记录，输出类型是 `String`。

```
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
    ...
}
```

使用此接口时，Java 运行时会将事件反序列化为具有输入类型的对象，并将输出序列化为文本。当内置序列化与输入和输出类型配合使用时，请使用此接口。

要使用您自己的序列化，您可以实现 `RequestStreamHandler` 接口。使用此接口，Lambda 将向您的处理程序传递输入流和输出流。处理程序从输入流读取字节，写到输出流，并返回 void。有关使用 Java 21 运行时的示例，请参阅 [HandlerStream.java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic/src/main/java/example/HandlerStream.java)。

如果您在 Java 函数中只使用基本类型和泛型类型（即 `String`、`Integer`、`List` 或 `Map`），则无需实现接口。例如，如果您的函数接受 `Map<String, String>` 输入并返回 `String`，则您的类定义和处理程序签名可能如下所示：

```
public class ExampleHandler {
    public String handleRequest(Map<String, String> input, Context context) {
        ...
    }
}
```

此外，当您不实现接口时，[上下文](java-context.md)对象是可选的。例如，您的类定义和处理程序签名可能如下所示：

```
public class NoContextHandler {
   public String handleRequest(Map<String, String> input) {
        ...
   }
}
```

## 处理程序命名约定
<a name="java-example-naming"></a>

对于 Java Lambda 函数，如果您要实现 `RequestHandler` 或 `RequestStreamHandler` 接口，则主处理程序方法必须命名为 `handleRequest`。此外，请在 `handleRequest` 方法上方添加 `@Override` 标签。将函数部署到 Lambda 时，请按以下格式在函数配置中指定主处理程序：
+ *<package>*.*<Class>*：例如，`example.OrderHandler`。

对于未实现 `RequestHandler` 或 `RequestStreamHandler` 接口的 Java Lambda 函数，您可以使用任何处理程序名称。将函数部署到 Lambda 时，请按以下格式在函数配置中指定主处理程序：
+ *<package>*.*<Class>*::*<handler\$1method\$1name>*：例如，`example.Handler::mainHandler`。

## 定义和访问输入事件对象
<a name="java-handler-input"></a>

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

**注意**  
Java 记录是 Java 17 运行时及更高版本独有的一项功能。在所有 Java 运行时系统中，您可以使用类来表示事件数据。在这种情况下，您可以使用如 [jackson](https://github.com/FasterXML/jackson) 之类的库来反序列化 JSON 输入。

### 其他输入事件类型
<a name="java-input-event-types"></a>

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](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events) 库中的类型。例如，如果 Amazon Simple Queue Service（SQS）调用了您的 Lambda 函数，则使用 `SQSEvent` 对象作为输入。

## 访问和使用 Lambda 上下文对象
<a name="java-example-context"></a>

Lambda [上下文对象](java-context.md)包含有关调用、函数和执行环境的信息。在此示例中，上下文对象的类型为 `com.amazonaws.services.lambda.runtime.Context`，是主处理程序函数的第二个参数。

```
public String handleRequest(Order event, Context context) {
    ...
}
```

如果您的类实现了 [RequestHandler](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestHandler.java) 或 [RequestStreamHandler](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestStreamHandler.java) 接口，则上下文对象是必需参数。否则，上下文对象是可选参数。有关有效接受处理程序签名的更多信息，请参阅[Java 处理程序的有效类定义](#java-handler-signatures)。

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

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

除了日志记录之外，您还可以使用上下文对象进行函数监控。有关上下文对象的更多信息，请参阅[使用 Lambda 上下文对象检索 Java 函数信息](java-context.md)。

## 在处理程序中使用 AWS SDK for Java v2
<a name="java-example-sdk-usage"></a>

通常，您将使用 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)));
```

## 评估环境变量
<a name="java-example-envvars"></a>

在处理程序代码中，您可以使用 `System.getenv()` 方法引用任何[环境变量](configuration-envvars.md)。在此示例中，我们使用以下代码行引用已定义的 `RECEIPT_BUCKET` 环境变量：

```
String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
    throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}
```

## 使用全局状态
<a name="java-handler-state"></a>

在首次调用您的函数之前，Lambda 会在[初始化阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib)运行您的静态代码和类构造函数。初始化期间创建的资源在两次调用之间保留在内存中，因此您可以避免每次调用函数时都必须创建这些资源的情况。

在示例代码中，S3 客户端初始化代码位于主处理程序方法之外。运行时会在函数处理第一个事件之前初始化客户端，之后所有的调用都可以重复使用该客户端。

## Java Lambda 函数的代码最佳实践
<a name="java-best-practices"></a>

在构建 Lambda 函数时，请遵循以下列表中的指南，采用最佳编码实践：
+ **从核心逻辑中分离 Lambda 处理程序。**这样您可以创建更容易进行单元测试的函数。
+ **控制函数部署包中的依赖项。**AWS Lambda 执行环境包含许多库。Lambda 会定期更新这些库，以支持最新的功能组合和安全更新。这些更新可能会使 Lambda 函数的行为发生细微变化。要完全控制您的函数所用的依赖项，请使用部署程序包来打包所有依赖项。
+ **将依赖关系的复杂性降至最低。**首选在[执行环境](lambda-runtime-environment.md)启动时可以快速加载的更简单的框架。例如，首选更简单的 Java 依赖关系注入 (IoC) 框架，如 [Dagger](https://google.github.io/dagger/) 或 [Guice](https://github.com/google/guice)，而不是更复杂的 [Spring Framework](https://github.com/spring-projects/spring-framework)。
+ **将部署包大小精简为只包含运行时必要的部分。**这样会减少调用前下载和解压缩部署程序包所需的时间。对于用 Java 编写的函数，请勿将整个 AWS SDK 库作为部署包的一部分上传，而是要根据所需的模块有选择地挑选软件开发工具包中的组件（例如 DynamoDB、Simple Storage Service (Amazon S3) 软件开发工具包模块和 [Lambda 核心库](https://github.com/aws/aws-lambda-java-libs)）。

**利用执行环境重用来提高函数性能。**连接软件开发工具包 (SDK) 客户端和函数处理程序之外的数据库，并在 `/tmp` 目录中本地缓存静态资产。由函数的同一实例处理的后续调用可重用这些资源。这样就可以通过缩短函数运行时间来节省成本。

为了避免调用之间潜在的数据泄露，请不要使用执行环境来存储用户数据、事件或其他具有安全影响的信息。如果您的函数依赖于无法存储在处理程序的内存中的可变状态，请考虑为每个用户创建单独的函数或单独的函数版本。

**使用 keep-alive 指令来维护持久连接。**Lambda 会随着时间的推移清除空闲连接。在调用函数时尝试重用空闲连接会导致连接错误。要维护您的持久连接，请使用与运行时关联的 keep-alive 指令。有关示例，请参阅[在 Node.js 中通过 Keep-Alive 重用连接](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html)。

**使用[环境变量](configuration-envvars.md)将操作参数传递给函数。**例如，您在写入 Amazon S3 存储桶时，不应对要写入的存储桶名称进行硬编码，而应将存储桶名称配置为环境变量。

**避免在 Lambda 函数中使用递归调用**，在这种情况下，函数会调用自己或启动可能再次调用该函数的进程。这可能会导致意想不到的函数调用量和升级成本。如果您看到意外的调用量，请立即将函数保留并发设置为 `0` 来限制对函数的所有调用，同时更新代码。

Lambda 函数代码中**不要使用非正式的非公有 API**。对于 AWS Lambda 托管式运行时，Lambda 会定期为 Lambda 的内部 API 应用安全性和功能更新。这些内部 API 更新可能不能向后兼容，会导致意外后果，例如，假设您的函数依赖于这些非公有 API，则调用会失败。请参阅 [API 参考](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)以查看公开发布的 API 列表。

**编写幂等代码。**为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息，请参阅[如何使我的 Lambda 函数具有幂等性？](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/)。
+ **避免使用 Java DNS 缓存。**Lambda 函数已经缓存了 DNS 响应。如果您使用了另一个 DNS 缓存，则可能会出现连接超时。

  `java.util.logging.Logger` 类可以间接启用 JVM DNS 缓存。要覆盖默认设置，请在初始化 `logger` 之前将 [networkaddress.cache.ttl](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/InetAddress.html#inetaddress-caching-heading) 设置为 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 函数](java-package.md)：

# 使用 .zip 或 JAR 文件归档部署 Java Lambda 函数
<a name="java-package"></a>

您的 AWS Lambda 函数代码由脚本或编译的程序及其依赖项组成。您可以使用*部署程序包*将函数代码部署到 Lambda。Lambda 支持两种类型的部署程序包：容器镜像和 .zip 文件归档。

本页将介绍如何将部署程序包创建为 .zip 文件或 Jar 文件，然后使用该部署程序包通过 AWS Lambda (AWS Command Line Interface) 将函数代码部署到 AWS CLI。

**重要**  
Java 25 引入了对提前编译（AOT）缓存的支持。我们强烈建议在将函数以 .zip 或 JAR 文件存档部署时，不要使用 AOT 缓存，因为这些缓存可能会在 Lambda 更新托管运行时的过程中导致意外行为。有关更多信息，请参阅 [提前编译（AOT）和 CDS 缓存](java-customization.md#aot-cds-caches)。

**Topics**
+ [先决条件](#java-package-prereqs)
+ [工具和库](#java-package-libraries)
+ [使用 Gradle 构建部署程序包](#java-package-gradle)
+ [对依赖项使用层](#java-package-layers)
+ [使用 Maven 构建部署程序包](#java-package-maven)
+ [使用 Lambda 控制台上传部署包](#java-package-console)
+ [使用 AWS CLI 上传部署包](#java-package-cli)
+ [使用 AWS SAM 上传部署程序包](#java-package-cloudformation)

## 先决条件
<a name="java-package-prereqs"></a>

AWS CLI 是一种开源工具，让您能够在命令行 Shell 中使用命令与 AWS 服务进行交互。要完成本节中的步骤，您必须拥有 [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

## 工具和库
<a name="java-package-libraries"></a>

AWS 提供以下适用于 Java 函数的库。这些库可通过 [Maven 中央存储库](https://search.maven.org/search?q=g:com.amazonaws)获得。
+ [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-core)（必需）– 定义处理程序方法接口和运行时传递给处理程序的上下文对象。如果您定义自己的输入类型，则这是您唯一需要的库。
+ [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events) – 调用 Lambda 函数服务的事件的输入类型。
+ [com.amazonaws:aws-lambda-java-log4j2](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-log4j2) – Apache Log4j 2 的 Appender 库，可用于将当前调用的请求 ID 添加到[函数日志](java-logging.md)中。
+ [适用于 Java 2.0 的AWS开发工具包](https://github.com/aws/aws-sdk-java-v2)：适用于 Java 编程语言的官方AWS开发工具包。

将这些库添加到您的构建定义中，如下所示：

------
#### [ Gradle ]

```
dependencies {
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
    implementation 'com.amazonaws:aws-lambda-java-events:3.11.1'
    runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1'
}
```

------
#### [ Maven ]

```
  <dependencies>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-core</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-events</artifactId>
      <version>3.11.1</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-log4j2</artifactId>
      <version>1.5.1</version>
    </dependency>
  </dependencies>
```

------

要创建部署程序包，请将函数代码和依赖项编译成单个 .zip 文件或 Java 存档 (JAR) 文件。对于 Gradle，[请使用 `Zip` 构建类型](#java-package-gradle)。对于 Apache Maven，[请使用 Maven Shade 插件](#java-package-maven)。要上传部署包，请使用 Lambda 控制台、Lambda API 或 AWS Serverless Application Model（AWS SAM）。

**注意**  
为了减小部署程序包的大小，请将函数的依赖项打包到层中。层可让您独立管理依赖项，可供多个函数使用，并可与其他账户共享。有关更多信息，请参阅 [使用层管理 Lambda 依赖项](chapter-layers.md)。

## 使用 Gradle 构建部署程序包
<a name="java-package-gradle"></a>

要创建包含 Gradle 中函数代码和依赖项的部署包，请使用 `Zip` 构建类型。这是来自[完整示例 build.gradle 文件](https://github.com/awsdocs/aws-lambda-developer-guide/blob/main/sample-apps/s3-java/build.gradle)的示例：

**Example build.gradle – 构建任务**  

```
task buildZip(type: Zip) {
    into('lib') {
        from(jar)
        from(configurations.runtimeClasspath)
    }
}
```

此构建配置在 `build/distributions` 目录中生成部署程序包。在 `into('lib')` 语句中，`jar` 任务将包含主类的 jar 归档组装到名为 `lib` 的文件夹中。此外，`configurations.runtimeClassPath` 任务将依赖项库从构建的类路径复制到同一 `lib` 文件夹中。

**Example build.gradle – 依赖项**  

```
dependencies {
    ...
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
    implementation 'com.amazonaws:aws-lambda-java-events:3.11.1'
    implementation 'org.apache.logging.log4j:log4j-api:2.17.1'
    implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
    runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.17.1'
    runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1'
    ...
}
```

Lambda 按 Unicode 字母顺序加载 JAR 文件。如果 `lib` 目录中的多个 JAR 文件包含相同的类，则使用第一个。可以使用以下 shell 脚本来识别重复类：

**Example test-zip.sh**  

```
mkdir -p expanded
unzip path/to/my/function.zip -d expanded
find ./expanded/lib -name '*.jar' | xargs -n1 zipinfo -1 | grep '.*.class' | sort | uniq -c | sort
```

## 对依赖项使用层
<a name="java-package-layers"></a>

您可以使用层打包函数的依赖项，以使部署包保持较小并独立管理依赖项。有关更多信息，请参阅 [使用 Java Lambda 函数的层](java-layers.md)。

## 使用 Maven 构建部署程序包
<a name="java-package-maven"></a>

要使用 Maven 构建部署程序包，请使用 [Maven Shade 插件](https://maven.apache.org/plugins/maven-shade-plugin/)。该插件创建一个包含编译的函数代码及其所有依赖项的 JAR 文件。

**Example pom.xml – 插件配置**  

```
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.2</version>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
```

要构建部署程序包，请使用 `mvn package` 命令。

```
[INFO] Scanning for projects...
[INFO] -----------------------< com.example:java-maven >-----------------------
[INFO] Building java-maven-function 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ java-maven ---
[INFO] Building jar: target/java-maven-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-shade-plugin:3.2.2:shade (default) @ java-maven ---
[INFO] Including com.amazonaws:aws-lambda-java-core:jar:1.2.2 in the shaded jar.
[INFO] Including com.amazonaws:aws-lambda-java-events:jar:3.11.1 in the shaded jar.
[INFO] Including joda-time:joda-time:jar:2.6 in the shaded jar.
[INFO] Including com.google.code.gson:gson:jar:2.8.6 in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing target/java-maven-1.0-SNAPSHOT.jar with target/java-maven-1.0-SNAPSHOT-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  8.321 s
[INFO] Finished at: 2020-03-03T09:07:19Z
[INFO] ------------------------------------------------------------------------
```

此命令在 `target` 目录中生成 JAR 文件。

**注意**  
如果您使用的是[多版本 JAR（MRJAR）](https://openjdk.org/jeps/238)，则必须在 `lib` 目录中包含 MRJAR（即由 Maven Shade 插件生成的着色 JAR），并在将部署包上传到 Lambda 之前对其进行压缩。否则，Lambda 可能无法正确解压 JAR 文件，从而导致 `MANIFEST.MF` 文件被忽略。

如果您使用 Appender 库 (`aws-lambda-java-log4j2`)，还必须为 Maven Shade 插件配置一个转换器。转换器库合并同时出现在 Appender 库和 Log4j 中的缓存文件的版本。

**Example pom.xml – 具有 Log4j 2 Appender 的插件配置**  

```
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.2</version>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer">
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>com.github.edwgiz</groupId>
            <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
            <version>2.13.0</version>
          </dependency>
        </dependencies>
      </plugin>
```

## 使用 Lambda 控制台上传部署包
<a name="java-package-console"></a>

 要创建新函数，必须先在控制台中创建该函数，然后上传您的 .zip 或 JAR 文件。要更新现有函数，请打开函数页面，然后按照相同的步骤添加更新的 .zip 或 JAR 文件。

 如果您的部署包文件小于 50MB，则可以通过直接从本地计算机上传该文件来创建或更新函数。对于大于 50MB 的 .zip 或 JAR 文件，必须首先将您的程序包上传到 Amazon S3 存储桶。有关如何使用 AWS 管理控制台 将文件上传到 Amazon S3 存储桶的说明，请参阅 [Amazon S3 入门](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html)。要使用 AWS CLI 上传文件，请参阅《AWS CLI 用户指南**》中的[移动对象](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move)。

**注意**  
您无法更改现有函数的[部署包类型](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html#lambda-CreateFunction-request-PackageType)（.zip 或容器映像）。例如，您无法将容器映像函数转换为使用 .zip 文件归档。您必须创建新函数。

**创建新函数（控制台）**

1. 打开 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择**创建函数**。

1. 选择**从头开始创作**。

1. 在**基本信息**中，执行以下操作：

   1. 对于**函数名称**，输入函数的名称。

   1. 对于**运行时系统**，选择要使用的运行时系统。

   1. （可选）对于**架构**，选择要用于函数的指令集架构。默认架构为 x86\$164。确保您的函数的 .zip 部署包与您选择的指令集架构兼容。

1. （可选）在 **Permissions**（权限）下，展开 **Change default execution role**（更改默认执行角色）。您可以创建新的**执行角色**，也可以使用现有角色。

1. 选择**创建函数**。Lambda 使用您选择的运行时系统创建基本“Hello world”函数。

**从本地计算机上传 .zip 或 JAR 归档（控制台）**

1. 在 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)中，选择要为其上传 .zip 或 JAR 文件的函数。

1. 选择**代码**选项卡。

1. 在**代码源**窗格中，选择**上传自**。

1. 选择 **.zip 或 .jar 文件**。

1. 要上传 .zip 或 JAR 文件，请执行以下操作：

   1. 选择**上传**，然后在文件选择器中选择您的 .zip 或 JAR 文件。

   1. 选择**打开**。

   1. 选择**保存**。

**从 Amazon S3 存储桶上传 .zip 或 JAR 归档（控制台）**

1. 在 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)中，选择要为其上传新 .zip 或 JAR 文件的函数。

1. 选择**代码**选项卡。

1. 在**代码源**窗格中，选择**上传自**。

1. 选择 **Amazon S3 位置**。

1. 粘贴 .zip 文件的 Amazon S3 链接 URL，然后选择**保存**。

## 使用 AWS CLI 上传部署包
<a name="java-package-cli"></a>

 您可以使用 [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 创建新函数或使用 .zip 或 JAR 文件更新现有函数。使用 [create-function](https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) 和 [update-function-code](https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) 命令部署 .zip 或 JAR 程序包。如果您的文件小于 50MB，则可以从本地生成计算机上的文件位置上传程序包。对于较大的文件，必须从 Amazon S3 存储桶上传 .zip 或 JAR 程序包。有关如何使用 AWS CLI 将文件上传到 Amazon S3 存储桶的说明，请参阅《AWS CLI 用户指南**》中的[移动对象](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move)。

**注意**  
如果您使用 AWS CLI 从 Amazon S3 存储桶上传 .zip 或 JAR 文件，则该存储桶必须与您的函数位于同一个 AWS 区域中。

 要通过 AWS CLI 使用 .zip 或 JAR 文件创建新函数，则必须指定以下内容：
+ 函数的名称 (`--function-name`)
+ 函数的运行时系统 (`--runtime`)
+ 函数的[执行角色](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) (`--role`) 的 Amazon 资源名称（ARN）
+ 函数代码 (`--handler`) 中处理程序方法的名称

 还必须指定 .zip 或 JAR 文件的位置。如果 .zip 或 JAR 文件位于本地生成计算机上的文件夹中，请使用 `--zip-file` 选项指定文件路径，如以下示例命令所示。

```
aws lambda create-function --function-name myFunction \
--runtime java25 --handler example.handler \
--role arn:aws:iam::123456789012:role/service-role/my-lambda-role \
--zip-file fileb://myFunction.zip
```

 要指定 .zip 文件在 Amazon S3 存储桶中的位置，请使用 `--code` 选项，如以下示例命令所示。您只需对版本控制对象使用 `S3ObjectVersion` 参数。

```
aws lambda create-function --function-name myFunction \
--runtime java25 --handler example.handler \
--role arn:aws:iam::123456789012:role/service-role/my-lambda-role \
--code S3Bucket=amzn-s3-demo-bucket,S3Key=myFileName.zip,S3ObjectVersion=myObjectVersion
```

 要使用 CLI 更新现有函数，请使用 `--function-name` 参数指定函数的名称。您还必须指定要用于更新函数代码的 .zip 文件的位置。如果 .zip 文件位于本地生成计算机上的文件夹中，请使用 `--zip-file` 选项指定文件路径，如以下示例命令所示。

```
aws lambda update-function-code --function-name myFunction \
--zip-file fileb://myFunction.zip
```

 要指定 .zip 文件在 Amazon S3 存储桶中的位置，请使用 `--s3-bucket` 和 `--s3-key` 选项，如以下示例命令所示。您只需对版本控制对象使用 `--s3-object-version` 参数。

```
aws lambda update-function-code --function-name myFunction \
--s3-bucket amzn-s3-demo-bucket --s3-key myFileName.zip --s3-object-version myObject Version
```

## 使用 AWS SAM 上传部署程序包
<a name="java-package-cloudformation"></a>

您可以使用 AWS SAM 自动部署函数代码、配置和依赖项。AWS SAM 是 CloudFormation 的一个扩展，它提供用于定义无服务器应用程序的简化语法。以下示例模板在 Gradle 使用的 `build/distributions` 目录中定义了一个包含部署程序包的函数。

**Example template.yml**  

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Lambda application that calls the Lambda API.
Resources:
  function:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      CodeUri: build/distributions/java-basic.zip
      Handler: example.Handler
      Runtime: java25
      Description: Java function
      MemorySize: 512
      Timeout: 10
      # Function's execution role
      Policies:
        - AWSLambdaBasicExecutionRole
        - AWSLambda_ReadOnlyAccess
        - AWSXrayWriteOnlyAccess
        - AWSLambdaVPCAccessExecutionRole
      Tracing: Active
```

要创建函数，请使用 `package` 和 `deploy` 命令。这些命令是对 AWS CLI 的自定义。它们包装其他命令以将部署程序包上载到 Amazon S3，使用对象 URI 重写模板，然后更新函数的代码。

以下示例脚本运行 Gradle 构建并上传其创建的部署程序包。它在您第一次运行它时创建一个 CloudFormation 堆栈。如果堆栈已经存在，脚本会更新它。

**Example deploy.sh**  

```
#!/bin/bash
set -eo pipefail
aws cloudformation package --template-file template.yml --s3-bucket MY_BUCKET --output-template-file out.yml
aws cloudformation deploy --template-file out.yml --stack-name java-basic --capabilities CAPABILITY_NAMED_IAM
```

有关完整的工作示例，请参阅以下示例应用程序。

**Java 中的 Lambda 应用程序示例**
+ [example-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/example-java) – 一个 Java 函数，演示如何使用 Lambda 来处理顺序。此函数说明了如何定义和反序列化自定义输入事件对象，如何使用 AWS SDK 和输出日志记录。
+ [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) – 具有单元测试和变量日志记录配置的最小 Java 函数的集合。
+ [java-events](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events) – Java 函数的集合，其中包含用于处理来自 Amazon API Gateway、Amazon SQS 和 Amazon Kinesis 等各种服务的事件的框架代码。这些函数使用最新版本的 [aws-lambda-events](#java-package) 库（3.0.0 及更新版本）。这些示例不需要 AWS 开发工具包作为依赖项。
+ [s3-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java) – 此 Java 函数可处理来自 Amazon S3 的通知事件，并使用 Java 类库（JCL）从上传的图像文件创建缩略图。
+ [layer-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-java) – 一个 Java 函数，说明如何使用 Lambda 层将依赖项与核心函数代码分开打包。

# 使用容器镜像部署 Java Lambda 函数
<a name="java-image"></a>

有三种方法可以为 Java Lambda 函数构建容器映像：
+ [使用 Java 的 AWS 基本映像](#java-image-instructions)

  [AWS 基本映像](images-create.md#runtimes-images-lp)会预加载一个语言运行时系统、一个用于管理 Lambda 和函数代码之间交互的运行时系统接口客户端，以及一个用于本地测试的运行时系统接口仿真器。
+ [使用 AWS 仅限操作系统的基础镜像](images-create.md#runtimes-images-provided)

  [AWS 仅限操作系统的运行时系统](https://gallery.ecr.aws/lambda/provided)包含 Amazon Linux 发行版和[运行时系统接口模拟器](https://github.com/aws/aws-lambda-runtime-interface-emulator/)。这些镜像通常用于为编译语言（例如 [Go](go-image.md#go-image-provided) 和 [Rust](lambda-rust.md)）以及 Lambda 未提供基础映像的语言或语言版本（例如 Node.js 19）创建容器镜像。您也可以使用仅限操作系统的基础映像来实施[自定义运行时系统](runtimes-custom.md)。要使映像与 Lambda 兼容，您必须在映像中包含 [Java 的运行时系统接口客户端](#java-image-clients)。
+ [使用非 AWS 基本映像](#java-image-clients)

  您还可以使用其他容器注册表的备用基本映像，例如 Alpine Linux 或 Debian。您还可以使用您的组织创建的自定义映像。要使映像与 Lambda 兼容，您必须在映像中包含 [Java 的运行时系统接口客户端](#java-image-clients)。

**提示**  
要缩短 Lambda 容器函数激活所需的时间，请参阅 Docker 文档中的[使用多阶段构建](https://docs.docker.com/build/building/multi-stage/)。要构建高效的容器映像，请遵循[编写 Dockerfiles 的最佳实践](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)。

此页面介绍了如何为 Lambda 构建、测试和部署容器映像。

**Topics**
+ [Java AWS 基本映像](#java-image-base)
+ [使用 Java 的 AWS 基本映像](#java-image-instructions)
+ [将备用基本映像与运行时系统接口客户端配合使用](#java-image-clients)

## Java AWS 基本映像
<a name="java-image-base"></a>

AWS 为 Java 提供了以下基本镜像：


| 标签 | 运行时 | 操作系统 | Dockerfile | 弃用 | 
| --- | --- | --- | --- | --- | 
| 25 | Java 25 | Amazon Linux 2023 | [GitHub 上的适用于 Java 25 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java25/Dockerfile.java25) |   2029 年 6 月 30 日   | 
| 21 | Java 21 | Amazon Linux 2023 | [GitHub 上的适用于 Java 21 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java21/Dockerfile.java21) |   2029 年 6 月 30 日   | 
| 17 | Java 17 | Amazon Linux 2 | [GitHub 上的适用于 Java 17 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java17/Dockerfile.java17) |   2027 年 6 月 30 日   | 
| 11 | Java 11 | Amazon Linux 2 | [GitHub 上的适用于 Java 11 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java11/Dockerfile.java11) |   2027 年 6 月 30 日   | 
| 8.al2 | Java 8 | Amazon Linux 2 | [GitHub 上的适用于 Java 8 的 Dockerfile](https://github.com/aws/aws-lambda-base-images/blob/java8.al2/Dockerfile.java8.al2) |   2027 年 6 月 30 日   | 

Amazon ECR 存储库：[gallery.ecr.aws/lambda/java](https://gallery.ecr.aws/lambda/java)

Java 21 及更高版本的基础映像基于 [Amazon Linux 2023 最小容器映像](https://docs.aws.amazon.com/linux/al2023/ug/minimal-container.html)。早期的基础映像使用 Amazon Linux 2。与 Amazon Linux 2 相比，AL2023 具有多项优势，包括较小的部署占用空间以及 `glibc` 等更新版本的库。

基于 AL2023 的映像使用 `microdnf`（符号链接为 `dnf`）作为软件包管理器，而不是 Amazon Linux 2 中的默认软件包管理器 `yum`。`microdnf` 是 `dnf` 的独立实现。有关基于 AL2023 的映像中已包含软件包的列表，请参阅[比较 Amazon Linux 2023 容器映像上安装的软件包](https://docs.aws.amazon.com/linux/al2023/ug/al2023-container-image-types.html)中的**最小容器**列。有关 AL2023 和 Amazon Linux 2 之间区别的更多信息，请参阅 AWS Compute Blog 上的 [Introducing the Amazon Linux 2023 runtime for AWS Lambda](https://aws.amazon.com/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/)。

**注意**  
要在本地运行基于 AL2023 的映像，包括使用 AWS Serverless Application Model（AWS SAM），您必须使用 Docker 版本 20.10.10 或更高版本。

## 使用 Java 的 AWS 基本映像
<a name="java-image-instructions"></a>

### 先决条件
<a name="java-image-prerequisites"></a>

要完成本节中的步骤，您必须满足以下条件：
+ Java（例如，[Amazon Corretto](https://aws.amazon.com/corretto)）
+ [Apache Maven](https://maven.apache.org/) 或 [Gradle](https://gradle.org/install/)
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker)（最低版本 25.0.0）
+ Docker [buildx 插件](https://github.com/docker/buildx/blob/master/README.md)。

### 从基本映像创建映像
<a name="java-image-create"></a>

------
#### [ Maven ]

1. 运行以下命令，以使用[适用于 Lambda 的原型](https://github.com/aws/aws-sdk-java-v2/tree/master/archetypes/archetype-lambda)创建 Maven 项目。以下参数为必需参数：
   + **服务** – 在 Lambda 函数中使用的 AWS 服务 客户端。有关可用源列表，请参阅 GitHub 上的 [aws-sdk-java-v2/services](https://github.com/aws/aws-sdk-java-v2/tree/master/services)。
   + **区域** – 要在其中创建 Lambda 函数的 AWS 区域。
   + **groupId** – 应用程序的完整程序包命名空间。
   + **artifactId** – 项目名称。这将成为项目的目录的名称。

   在 Linux 和 macOS 中，运行以下命令：

   ```
   mvn -B archetype:generate \
      -DarchetypeGroupId=software.amazon.awssdk \
      -DarchetypeArtifactId=archetype-lambda -Dservice=s3 -Dregion=US_WEST_2 \
      -DgroupId=com.example.myapp \
      -DartifactId=myapp
   ```

   在 PowerShell 中，运行以下命令：

   ```
   mvn -B archetype:generate `
      "-DarchetypeGroupId=software.amazon.awssdk" `
      "-DarchetypeArtifactId=archetype-lambda" "-Dservice=s3" "-Dregion=US_WEST_2" `
      "-DgroupId=com.example.myapp" `
      "-DartifactId=myapp"
   ```

   适用于 Lambda 的 Maven 原型已预配置为使用 Java SE 8 进行编译，并包含对 适用于 Java 的 AWS SDK 的依赖项。如果使用其他原型或其他方法创建项目，则必须[为 Maven 配置 Java 编译器](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#configure-maven-compiler)并[将开发工具包声明为依赖项](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#configure-maven-compiler)。

1. 打开 `myapp/src/main/java/com/example/myapp` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。您可以使用提供的示例代码进行测试，也可以将其替换为您自己的代码。

1. 导航回项目的根目录，然后创建一个具有以下配置的新 Dockerfile：
   + 将 `FROM` 属性设置为[基本映像的 URI](https://gallery.ecr.aws/lambda/java)。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/java:21
     
   # Copy function code and runtime dependencies from Maven layout
   COPY target/classes ${LAMBDA_TASK_ROOT}
   COPY target/dependency/* ${LAMBDA_TASK_ROOT}/lib/
       
   # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
   CMD [ "com.example.myapp.App::handleRequest" ]
   ```

1. 编译项目并收集运行时系统依赖项。

   ```
   mvn compile dependency:copy-dependencies -DincludeScope=runtime
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

------
#### [ Gradle ]

1. 为项目创建一个目录，然后切换到该目录。

   ```
   mkdir example
   cd example
   ```

1. 运行以下命令来让 Gradle 在环境中的 `example` 目录中生成新的 Java 应用程序项目。在**选择构建脚本 DSL** 中，选择 **2: Groovy**。

   ```
   gradle init --type java-application
   ```

1. 打开 `/example/app/src/main/java/example` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。您可以使用以下示例代码进行测试，也可以将其替换为您自己的代码。  
**Example App.java**  

   ```
   package com.example;
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   public class App implements RequestHandler<Object, String> {
       public String handleRequest(Object input, Context context) {
           return "Hello world!";
       }
   }
   ```

1. 打开 `build.gradle` 文件。如果您使用的是上一步中的示例函数代码，请将 `build.gradle` 的内容替换为以下内容。如果您使用的是自己的函数代码，请根据需要修改 `build.gradle` 文件。  
**Example build.gradle (Groovy DSL)**  

   ```
   plugins {
     id 'java'
   }
   group 'com.example'
   version '1.0-SNAPSHOT'
   sourceCompatibility = 1.8
   repositories {
     mavenCentral()
   }
   dependencies {
     implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
   }
   jar {
     manifest {
         attributes 'Main-Class': 'com.example.App'
     }
   }
   ```

1. 第 2 步中的 `gradle init` 命令还在 `app/test` 目录中生成了一个虚拟测试用例。在本教程中，请删除 `/test` 目录，从而跳过运行测试。

1. 构建项目。

   ```
   gradle build
   ```

1. 在项目的根目录 (`/example`) 中，创建一个具有以下配置的 Dockerfile：
   + 将 `FROM` 属性设置为[基本映像的 URI](https://gallery.ecr.aws/lambda/java)。
   + 使用 COPY 命令将函数代码和运行时系统依赖项复制到 `{LAMBDA_TASK_ROOT}`，此为 [Lambda 定义的环境变量](configuration-envvars.md#configuration-envvars-runtime)。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/java:21
     
   # Copy function code and runtime dependencies from Gradle layout
   COPY app/build/classes/java/main ${LAMBDA_TASK_ROOT}
     
   # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
   CMD [ "com.example.App::handleRequest" ]
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

------

### （可选）在本地测试映像
<a name="java-image-test"></a>

1. 使用 **docker run** 命令启动 Docker 映像。在此示例中，`docker-image` 是映像名称，`test` 是标签。

   ```
   docker run --platform linux/amd64 -p 9000:8080 docker-image:test
   ```

   此命令会将映像作为容器运行，并在 `localhost:9000/2015-03-31/functions/function/invocations` 创建本地端点。
**注意**  
如果为 ARM64 指令集架构创建 Docker 映像，请务必使用 `--platform linux/arm64` 选项，而不是 `--platform linux/amd64` 选项。

1. 在新的终端窗口中，将事件发布到本地端点。

------
#### [ Linux/macOS ]

   在 Linux 和 macOS 中，运行以下 `curl` 命令：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   在 PowerShell 中，运行以下 `Invoke-WebRequest` 命令：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. 获取容器 ID。

   ```
   docker ps
   ```

1. 使用 [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) 命令停止容器。在此命令中，将 `3766c4ab331c` 替换为上一步中的容器 ID。

   ```
   docker kill 3766c4ab331c
   ```

### 部署映像
<a name="java-image-deploy"></a>

**将映像上传到 Amazon ECR 并创建 Lambda 函数**

1. 运行 [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) 命令，以针对 Amazon ECR 注册表进行 Docker CLI 身份验证。
   + 将 `--region` 值设置为要在其中创建 Amazon ECR 存储库的 AWS 区域。
   + 将 `111122223333` 替换为您的 AWS 账户 ID。

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. 使用 [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) 命令在 Amazon ECR 中创建存储库。

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**注意**  
Amazon ECR 存储库必须与 Lambda 函数位于同一 AWS 区域 内。

   如果成功，您将会看到如下响应：

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. 从上一步的输出中复制 `repositoryUri`。

1. 运行 [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) 命令，将本地映像作为最新版本标记到 Amazon ECR 存储库中。在此命令中：
   + `docker-image:test` 是 Docker 映像的名称和[标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。这是您在 `docker build` 命令中指定的映像名称和标签。
   + 将 `<ECRrepositoryUri>` 替换为复制的 `repositoryUri`。确保 URI 末尾包含 `:latest`。

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   示例：

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 运行 [docker push](https://docs.docker.com/engine/reference/commandline/push/) 命令，以将本地映像部署到 Amazon ECR 存储库。确保存储库 URI 末尾包含 `:latest`。

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 如果您还没有函数的执行角色，请[创建执行角色](lambda-intro-execution-role.md#permissions-executionrole-api)。在下一步中，您需要提供角色的 Amazon 资源名称（ARN）。

1. 创建 Lambda 函数。对于 `ImageUri`，指定之前的存储库 URI。确保 URI 末尾包含 `:latest`。

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**注意**  
只要映像与 Lambda 函数位于同一区域内，您就可以使用其他 AWS 账户中的映像创建函数。有关更多信息，请参阅 [Amazon ECR 跨账户权限](images-create.md#configuration-images-xaccount-permissions)。

1. 调用函数。

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   应出现如下响应：

   ```
   {
     "ExecutedVersion": "$LATEST", 
     "StatusCode": 200
   }
   ```

1. 要查看函数的输出，请检查 `response.json` 文件。

要更新函数代码，您必须再次构建映像，将新映像上传到 Amazon ECR 存储库，然后使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令将映像部署到 Lambda 函数。

Lambda 会将映像标签解析为特定的映像摘要。这意味着，如果您将用于部署函数的映像标签指向 Amazon ECR 中的新映像，则 Lambda 不会自动更新该函数以使用新映像。

要将新映像部署到相同的 Lambda 函数，即使 Amazon ECR 中的映像标签保持不变，也必须使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令。在以下示例中，`--publish` 选项使用更新的容器映像创建函数的新版本。

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```

## 将备用基本映像与运行时系统接口客户端配合使用
<a name="java-image-clients"></a>

如果使用[仅限操作系统的基础映像](images-create.md#runtimes-images-provided)或者备用基础映像，则必须在映像中包括运行时系统接口客户端。运行时系统接口客户端可扩展 [运行时 API](runtimes-api.md)，用于管理 Lambda 和函数代码之间的交互。

在 Dockerfile 中安装 Java 的运行时系统接口客户端，或作为项目中的依赖项安装。例如，要使用 Maven 程序包管理器安装运行时系统接口客户端，请将以下内容添加到您的 `pom.xml` 文件中：

```
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-runtime-interface-client</artifactId>
    <version>2.3.2</version>
</dependency>
```

有关程序包的详细信息，请参阅 Maven Central 存储库中的 [AWS Lambda Java 运行时系统接口客户端](https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-runtime-interface-client)。您还可以在 [AWS Lambda Java 支持库](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-runtime-interface-client) GitHub 存储库中查看运行时系统接口客户端源代码。

以下示例演示了如何使用 [Amazon Corretto 映像](https://gallery.ecr.aws/amazoncorretto/amazoncorretto)为 Java 构建容器映像。Amazon Corretto 是开放 Java 开发工具包（OpenJDK）的免费、多平台、生产就绪型分发版。Maven 项目将运行时系统接口客户端作为依赖项包括在内。

### 先决条件
<a name="java-alt-prerequisites"></a>

要完成本节中的步骤，您必须满足以下条件：
+ Java（例如，[Amazon Corretto](https://aws.amazon.com/corretto)）
+ [Apache Maven](https://maven.apache.org/)
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker)（最低版本 25.0.0）
+ Docker [buildx 插件](https://github.com/docker/buildx/blob/master/README.md)。

### 从备用基本映像创建映像
<a name="java-alt-create"></a>

1. 创建 Maven 项目。以下参数为必需参数：
   + **groupId** – 应用程序的完整程序包命名空间。
   + **artifactId** – 项目名称。这将成为项目的目录的名称。

------
#### [ Linux/macOS ]

   ```
   mvn -B archetype:generate \
      -DarchetypeArtifactId=maven-archetype-quickstart \
      -DgroupId=example \
      -DartifactId=myapp \
      -DinteractiveMode=false
   ```

------
#### [ PowerShell ]

   ```
   mvn -B archetype:generate `
      -DarchetypeArtifactId=maven-archetype-quickstart `
      -DgroupId=example `
      -DartifactId=myapp `
      -DinteractiveMode=false
   ```

------

1. 打开项目目录。

   ```
   cd myapp
   ```

1. 打开 `pom.xml` 文件并将相应内容替换为以下内容。此文件将 [aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-runtime-interface-client) 作为依赖项包括在内。或者，您可以在 Dockerfile 中安装运行时系统接口客户端。但是，最简单的方法是将库作为依赖项包含在内。

   ```
   <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>example</groupId>
     <artifactId>hello-lambda</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>hello-lambda</name>
     <url>http://maven.apache.org</url>
     <properties>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
     <dependencies>
       <dependency>
         <groupId>com.amazonaws</groupId>
         <artifactId>aws-lambda-java-runtime-interface-client</artifactId>
         <version>2.3.2</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-dependency-plugin</artifactId>
           <version>3.1.2</version>
           <executions>
             <execution>
               <id>copy-dependencies</id>
               <phase>package</phase>
               <goals>
                 <goal>copy-dependencies</goal>
               </goals>
             </execution>
           </executions>
         </plugin>
       </plugins>
     </build>
   </project>
   ```

1. 打开 `myapp/src/main/java/com/example/myapp` 目录，找到 `App.java` 文件。这是适用于 Lambda 函数的代码。使用以下内容替换相应代码。  
**Example 函数处理程序**  

   ```
   package example;
   
   public class App {
       public static String sayHello() {
           return "Hello world!";
       }
   }
   ```

1. 第 1 步中的 `mvn -B archetype:generate` 命令还会在 `src/test` 目录中生成了一个虚拟测试用例。在本教程中，请完整删除生成的 `/test` 目录，从而跳过运行测试。

1. 导航回项目的根目录，然后创建一个新 Dockerfile。以下示例 Dockerfile 使用 [Amazon Corretto 映像](https://gallery.ecr.aws/amazoncorretto/amazoncorretto)。Amazon Corretto 是 OpenJDK 的免费、多平台、生产就绪型分发版。
   + 将 `FROM` 属性设置为基本映像的 URI。
   + 将 `ENTRYPOINT` 设置为您希望 Docker 容器在启动时运行的模块。在本例中，模块为运行时系统接口客户端。
   + 将 `CMD` 参数设置为 Lambda 函数处理程序。

   请注意，示例 Dockerfile 不包含 [USER 指令](https://docs.docker.com/reference/dockerfile/#user)。当您将容器映像部署到 Lambda 时，Lambda 会自动定义具有最低权限的默认 Linux 用户。这与标准 Docker 行为不同，标准 Docker 在未提供 `USER` 指令时默认为 `root` 用户。  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/amazoncorretto/amazoncorretto:21 as base
   
   # Configure the build environment
   FROM base as build
   RUN yum install -y maven
   WORKDIR /src
   
   # Cache and copy dependencies
   ADD pom.xml .
   RUN mvn dependency:go-offline dependency:copy-dependencies
   
   # Compile the function
   ADD . .
   RUN mvn package 
   
   # Copy the function artifact and dependencies onto a clean base
   FROM base
   WORKDIR /function
   
   COPY --from=build /src/target/dependency/*.jar ./
   COPY --from=build /src/target/*.jar ./
   
   # Set runtime interface client as default command for the container runtime
   ENTRYPOINT [ "/usr/bin/java", "-cp", "./*", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ]
   # Pass the name of the function handler as an argument to the runtime
   CMD [ "example.App::sayHello" ]
   ```

1. 使用 [docker build](https://docs.docker.com/engine/reference/commandline/build/) 命令构建 Docker 映像。以下示例将映像命名为 `docker-image` 并为其提供 `test` [标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。要使您的映像与 Lambda 兼容，您必须使用 `--provenance=false` 选项。

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**注意**  
该命令指定了 `--platform linux/amd64` 选项，可确保无论生成计算机的架构如何，容器始终与 Lambda 执行环境兼容。如果打算使用 ARM64 指令集架构创建 Lambda 函数，请务必将命令更改为使用 `--platform linux/arm64` 选项。

### （可选）在本地测试映像
<a name="java-alt-test"></a>

使用[运行时系统接口仿真器](https://github.com/aws/aws-lambda-runtime-interface-emulator/)在本地测试映像。您可以[将仿真器构建到映像中](https://github.com/aws/aws-lambda-runtime-interface-emulator/?tab=readme-ov-file#build-rie-into-your-base-image)，也可以使用以下程序将其安装在本地计算机上。

**在本地计算机上安装并运行运行时系统接口仿真器**

1. 从项目目录中，运行以下命令以从 GitHub 下载运行时系统接口仿真器（x86-64 架构）并将其安装在本地计算机上。

------
#### [ Linux/macOS ]

   ```
   mkdir -p ~/.aws-lambda-rie && \
       curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
       chmod +x ~/.aws-lambda-rie/aws-lambda-rie
   ```

   要安装 arm64 仿真器，请将上一条命令中的 GitHub 存储库 URL 替换为以下内容：

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------
#### [ PowerShell ]

   ```
   $dirPath = "$HOME\.aws-lambda-rie"
   if (-not (Test-Path $dirPath)) {
       New-Item -Path $dirPath -ItemType Directory
   }
         
   $downloadLink = "https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie"
   $destinationPath = "$HOME\.aws-lambda-rie\aws-lambda-rie"
   Invoke-WebRequest -Uri $downloadLink -OutFile $destinationPath
   ```

   要安装 arm64 模拟器，请将 `$downloadLink` 替换为以下内容：

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------

1. 使用 **docker run** 命令启动 Docker 映像。请注意以下几点：
   + `docker-image` 是映像名称，`test` 是标签。
   + `/usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello` 是 `ENTRYPOINT`，后跟您 Dockerfile 中的 `CMD`。

------
#### [ Linux/macOS ]

   ```
   docker run --platform linux/amd64 -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
       --entrypoint /aws-lambda/aws-lambda-rie \
       docker-image:test \
           /usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello
   ```

------
#### [ PowerShell ]

   ```
   docker run --platform linux/amd64 -d -v "$HOME\.aws-lambda-rie:/aws-lambda" -p 9000:8080 `
   --entrypoint /aws-lambda/aws-lambda-rie `
   docker-image:test `
       /usr/bin/java -cp './*' com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello
   ```

------

   此命令会将映像作为容器运行，并在 `localhost:9000/2015-03-31/functions/function/invocations` 创建本地端点。
**注意**  
如果为 ARM64 指令集架构创建 Docker 映像，请务必使用 `--platform linux/arm64` 选项，而不是 `--platform linux/amd64` 选项。

1. 将事件发布到本地端点。

------
#### [ Linux/macOS ]

   在 Linux 和 macOS 中，运行以下 `curl` 命令：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   在 PowerShell 中，运行以下 `Invoke-WebRequest` 命令：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   此命令使用空事件调用函数并返回响应。如果您使用自己的函数代码而不是示例函数代码，则可能需要使用 JSON 负载调用函数。示例：

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. 获取容器 ID。

   ```
   docker ps
   ```

1. 使用 [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) 命令停止容器。在此命令中，将 `3766c4ab331c` 替换为上一步中的容器 ID。

   ```
   docker kill 3766c4ab331c
   ```

### 部署映像
<a name="java-alt-deploy"></a>

**将映像上传到 Amazon ECR 并创建 Lambda 函数**

1. 运行 [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) 命令，以针对 Amazon ECR 注册表进行 Docker CLI 身份验证。
   + 将 `--region` 值设置为要在其中创建 Amazon ECR 存储库的 AWS 区域。
   + 将 `111122223333` 替换为您的 AWS 账户 ID。

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. 使用 [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) 命令在 Amazon ECR 中创建存储库。

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**注意**  
Amazon ECR 存储库必须与 Lambda 函数位于同一 AWS 区域 内。

   如果成功，您将会看到如下响应：

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. 从上一步的输出中复制 `repositoryUri`。

1. 运行 [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) 命令，将本地映像作为最新版本标记到 Amazon ECR 存储库中。在此命令中：
   + `docker-image:test` 是 Docker 映像的名称和[标签](https://docs.docker.com/engine/reference/commandline/build/#tag)。这是您在 `docker build` 命令中指定的映像名称和标签。
   + 将 `<ECRrepositoryUri>` 替换为复制的 `repositoryUri`。确保 URI 末尾包含 `:latest`。

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   示例：

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 运行 [docker push](https://docs.docker.com/engine/reference/commandline/push/) 命令，以将本地映像部署到 Amazon ECR 存储库。确保存储库 URI 末尾包含 `:latest`。

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. 如果您还没有函数的执行角色，请[创建执行角色](lambda-intro-execution-role.md#permissions-executionrole-api)。在下一步中，您需要提供角色的 Amazon 资源名称（ARN）。

1. 创建 Lambda 函数。对于 `ImageUri`，指定之前的存储库 URI。确保 URI 末尾包含 `:latest`。

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**注意**  
只要映像与 Lambda 函数位于同一区域内，您就可以使用其他 AWS 账户中的映像创建函数。有关更多信息，请参阅 [Amazon ECR 跨账户权限](images-create.md#configuration-images-xaccount-permissions)。

1. 调用函数。

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   应出现如下响应：

   ```
   {
     "ExecutedVersion": "$LATEST", 
     "StatusCode": 200
   }
   ```

1. 要查看函数的输出，请检查 `response.json` 文件。

要更新函数代码，您必须再次构建映像，将新映像上传到 Amazon ECR 存储库，然后使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令将映像部署到 Lambda 函数。

Lambda 会将映像标签解析为特定的映像摘要。这意味着，如果您将用于部署函数的映像标签指向 Amazon ECR 中的新映像，则 Lambda 不会自动更新该函数以使用新映像。

要将新映像部署到相同的 Lambda 函数，即使 Amazon ECR 中的映像标签保持不变，也必须使用 [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) 命令。在以下示例中，`--publish` 选项使用更新的容器映像创建函数的新版本。

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```

# 使用 Java Lambda 函数的层
<a name="java-layers"></a>

使用 [Lambda 层](chapter-layers.md)来打包要在多个函数中重复使用的代码和依赖项。层通常包含库依赖项、[自定义运行时系统](runtimes-custom.md)或配置文件。创建层涉及三个常见步骤：

1. 打包层内容。此步骤需要创建 .zip 文件存档，其中包含要在函数中使用的依赖项。

1. 在 Lambda 中创建层。

1. 将层添加到函数。

**Topics**
+ [打包层内容](#java-layers-package)
+ [在 Lambda 中创建层](#publishing-layer)
+ [将层添加到函数](#java-layer-adding)

## 打包层内容
<a name="java-layers-package"></a>

要创建层，请将您的包捆绑到满足以下要求的 .zip 文件存档中：
+ 确保 Maven 或 Gradle 引用的 Java 版本与要部署的函数的 Java 版本相同。例如，若要部署 Java 25 函数，`mvn -v` 命令应在输出中列出 Java 25。
+ 您的依赖项必须存储在 `java/lib` 目录中，该目录位于.zip文件的根级别下。有关更多信息，请参阅 [每个 Lambda 运行时的层路径](packaging-layers.md#packaging-layers-paths)。
+ 您的层中的包必须与 Linux 兼容。Lambda 函数在 Amazon Linux 上运行。

您可以创建包含第三方 Java 库或您自己的 Java 模块和包的层。以下程序使用 Maven。您也可以使用 Gradle 来打包您的层内容。

**要使用 Maven 依赖项来创建层**

1. 使用定义依赖项的 `pom.xml` 文件来创建一个 Apache Maven 项目。

   以下示例包括用于 JSON 处理的 [Jackson Databind](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind)。`<build>` 部分使用 [maven-dependency-plugin](https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-dependency-plugin) 为每个依赖项生成独立的 JAR 文件，而非将它们捆绑到一个 uber-jar 中。如果您想创建一个 uber-jar，可以使用 [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/)。  
**Example pom.xml**  

   ```
   <dependencies>
       <dependency>
           <groupId>com.fasterxml.jackson.core</groupId>
           <artifactId>jackson-databind</artifactId>
           <version>2.17.0</version>
       </dependency>
   </dependencies>
   
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.13.0</version>
               <configuration>
                   <source>21</source>
                   <target>21</target>
                   <release>21</release>
               </configuration>
           </plugin>
           
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-dependency-plugin</artifactId>
               <version>3.6.1</version>
               <executions>
                   <execution>
                       <id>copy-dependencies</id>
                       <phase>package</phase>
                       <goals>
                           <goal>copy-dependencies</goal>
                       </goals>
                       <configuration>
                           <outputDirectory>${project.build.directory}/lib</outputDirectory>
                       </configuration>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>
   ```

1. 构建项目。此命令将在 `target/lib/` 目录中创建所有的依赖项 JAR 文件。

   ```
   mvn clean package
   ```

1. 为您的层创建所需的目录结构：

   ```
   mkdir -p java/lib
   ```

1. 将依赖项 JAR 文件复制到 `java/lib`目录：

   ```
   cp target/lib/*.jar java/lib/
   ```

1. 压缩层内容：

------
#### [ Linux/macOS ]

   ```
   zip -r layer.zip java/
   ```

------
#### [ PowerShell ]

   ```
   Compress-Archive -Path .\java -DestinationPath .\layer.zip
   ```

------

   您的 .zip 文件的目录结构应如下所示：

   ```
   java/              
   └── lib/
       ├── jackson-databind-2.17.0.jar
       ├── jackson-core-2.17.0.jar
       └── jackson-annotations-2.17.0.jar
   ```
**注意**  
请确保您的 .zip 文件包含位于根级别下的 `java` 目录，且该目录内包含 `lib`。此结构旨在确保 Lambda 可以找到并导入您的库。每个依赖项都作为单独的 JAR 文件保存，而不是捆绑到一个 uber-jar 中。

## 在 Lambda 中创建层
<a name="publishing-layer"></a>

您可以使用 AWS CLI 或 Lambda 控制台发布层。

------
#### [ AWS CLI ]

运行 [publish-layer-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-layer-version.html) AWS CLI 命令以创建 Lambda 层：

```
aws lambda publish-layer-version --layer-name my-layer --zip-file fileb://layer.zip --compatible-runtimes java25
```

[兼容的运行时](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes)参数是可选的。指定后，Lambda 将使用此参数在 Lambda 控制台中筛选层。

------
#### [ Console ]

**创建层（控制台）**

1. 打开 Lambda 控制台的 [Layers page](https://console.aws.amazon.com/lambda/home#/layers)（层页面）。

1. 选择 **Create layer**（创建层）。

1. 选择**上传 .zip 文件**，然后上传您之前创建的 .zip 存档。

1. （可选）对于**兼容的运行时**，请选择与您用于构建层的 Java 版本相对应的 Java 运行时。

1. 选择**创建**。

------

## 将层添加到函数
<a name="java-layer-adding"></a>

------
#### [ AWS CLI ]

要将层附加到函数，请运行 [update-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-configuration.html) AWS CLI 命令。对于 `--layers` 参数，使用层 ARN。ARN 必须指定版本（例如 `arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1`）。有关更多信息，请参阅 [层和层版本](chapter-layers.md#lambda-layer-versions)。

```
aws lambda update-function-configuration --function-name my-function --cli-binary-format raw-in-base64-out --layers "arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1"
```

如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。

------
#### [ Console ]

**向函数添加层**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 向下滚动到**层**部分，然后选择**添加层**。

1. 在**选择层**下，选择**自定义层**，然后选择您的层。
**注意**  
如果您在创建层时没有添加[兼容的运行时](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes)，则您的层将不会在此处列出。您可以改为指定层 ARN。

1. 选择**添加**。

------

# 自定义 Lambda Java 函数的序列化
<a name="java-custom-serialization"></a>

Lambda [Java 托管式运行时](lambda-java.md#java-runtimes)支持 JSON 事件的自定义序列化。自定义序列化可以简化代码并有可能提高性能。

**Topics**
+ [何时使用自定义序列化](#custom-serialization-use-cases)
+ [实现自定义序列化](#implement-custom-serialization)
+ [测试自定义序列化](#test-custom-serialization)

## 何时使用自定义序列化
<a name="custom-serialization-use-cases"></a>

调用 Lambda 函数时，需要将输入事件数据反序列化为 Java 对象，并且需要将函数的输出序列化回可以作为函数响应返回的格式。Lambda Java 托管式运行时提供默认的序列化和反序列化功能，非常适合处理来自各种 AWS 服务的事件有效载荷，例如 Amazon API Gateway 和 Amazon Simple Queue Service（Amazon SQS）。要在函数中使用这些服务集成事件，请向项目中添加 [aws-java-lambda-events](https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-events) 依赖项。此 AWS 库包含表示这些服务集成事件的 Java 对象。

您也可以使用自己的对象来表示传递给 Lambda 函数的事件 JSON。托管式运行时会尝试将 JSON 序列化为对象的新实例，且该新实例具有其默认行为。如果默认序列化器无用例所需的行为，请使用自定义序列化。

例如，假设函数处理程序需要 `Vehicle` 类作为输入，并且此类具有以下结构：

```
public class Vehicle {
    private String vehicleType;
    private long vehicleId;
}
```

但是，JSON 事件有效载荷如下所示：

```
{
    "vehicle-type": "car",
    "vehicleID": 123
}
```

在这种情况下，托管式运行时中的默认序列化需要 JSON 属性名称与驼峰式大小写的 Java 类属性名称（`vehicleType`、`vehicleId`）相匹配。JSON 事件中的属性名称并非为驼峰式大小写（`vehicle-type`、`vehicleID`），因此必须使用自定义序列化。

## 实现自定义序列化
<a name="implement-custom-serialization"></a>

使用[服务提供程序接口](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)加载您选择的序列化器，而非托管式运行时的默认序列化逻辑。您可以使用标准 `RequestHandler` 接口将 JSON 事件有效载荷直接序列化为 Java 对象。

**要在 Lambda Java 函数中使用自定义序列化**

1. 添加 [aws-lambda-java-core](https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-core)（作为依赖项）。此库包括 [CustomPojoSerializer](https://github.com/aws/aws-lambda-java-libs/blob/main/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/CustomPojoSerializer.java) 接口，以及其他用于在 Lambda 中使用 Java 的接口定义。

1. 在项目的 `src/main/resources/META-INF/services/` 目录中创建名为 `com.amazonaws.services.lambda.runtime.CustomPojoSerializer` 的文件。

1. 在此文件中，指定自定义序列化器实现（该实现必须实现 `CustomPojoSerializer` 接口）的完全限定名称。示例：

   ```
   com.mycompany.vehicles.CustomLambdaSerialzer
   ```

1. 实现 `CustomPojoSerializer` 接口以提供自定义序列化逻辑。

1. 在 Lambda 函数中使用标准 `RequestHandler` 接口。托管式运行时将使用自定义序列化程序。

有关如何使用 fastJson、Gson、Moshi 和 jackson-jr 等常用库实现自定义序列化的更多示例，请参阅《AWS GitHub repository》中的 [custom-serialization](https://github.com/aws/aws-lambda-java-libs/tree/main/samples/custom-serialization) 示例。

## 测试自定义序列化
<a name="test-custom-serialization"></a>

测试函数，确保序列化和反序列化逻辑按预期运行。您可以使用 AWS Serverless Application Model 命令行接口（AWS SAMCLI）来模拟 Lambda 有效载荷的调用。在引入自定义序列化器时，此举可以帮助您快速测试和迭代函数。

1. 使用要调用函数的 JSON 事件有效载荷创建文件，然后调用 AWS SAM CLI。

1. 运行 [sam local invoke](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html ) 命令，在本地调用函数。示例：

   ```
   sam local invoke -e src/test/resources/event.json
   ```

有关更多信息，请参阅 [Locally invoke Lambda functions with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html)。

# 自定义 Lambda 函数的 Java 运行时启动行为
<a name="java-customization"></a>

本页将介绍特定于 AWS Lambda 中 Java 函数的设置。您可以使用这些设置来自定义 Java 运行时系统启动行为。这样可以减少总体函数延迟并提高总体函数性能，而无需修改任何代码。

**Topics**
+ [了解 `JAVA_TOOL_OPTIONS` 环境变量](#java-tool-options)
+ [Log4Shell 的 Log4j 补丁](#log4shell-patch)
+ [提前编译（AOT）和 CDS 缓存](#aot-cds-caches)

## 了解 `JAVA_TOOL_OPTIONS` 环境变量
<a name="java-tool-options"></a>

在 Java 中，Lambda 支持 `JAVA_TOOL_OPTIONS` 环境变量以在 Lambda 中设置其他命令行变量。您可以通过多种方式使用此环境变量，例如自定义分层编译设置。下一个示例演示了如何针对此使用案例使用 `JAVA_TOOL_OPTIONS` 环境变量。

### 示例：自定义分层编译设置
<a name="tiered-compilation"></a>

分层编译是 Java 虚拟机（JVM）的一项功能。您可以使用特定的分层编译设置来充分利用 JVM 的即时（JIT）编译器。通常，C1 编译器经过优化，可缩短启动时间。C2 编译器经过优化以获得最佳整体性能，但它也会占用更多内存，并且需要更长的时间才能实现。分层编译有 5 个不同级别。在级别 0 上，JVM 解释 Java 字节代码。在级别 4 上，JVM 使用 C2 编译器来分析在应用程序启动期间收集的分析数据。随着时间的推移，它会监控代码使用情况以确定最佳优化。

自定义分层编译级别有助于您优化 Java 函数的性能。对于执行速度较快的小型函数，将分层编译级别设置为 1 级可以通过让 JVM 使用 C1 编译器来帮助提升冷启动性能。该设置可以快速生成优化原生代码，但它不生成任何分析数据，也从不使用 C2 编译器。对于计算密集型的大型函数，将分层编译设置为 4 级可最大限度地提高整体性能，但会带来额外的内存消耗以及每次在 Lambda 执行环境预置后首次调用时的额外优化工作。

对于 Java 11 运行时及更低版本，Lambda 使用默认的 JVM 分层编译设置。对于 Java 17 和 Java 21，Lambda 将 JVM 配置为默认在级别 1 停止分层编译。从 Java 25 开始，Lambda 仍会默认在级别 1 停止分层编译，除非使用 SnapStart 或预置并发，在这种情况下，将使用默认的 JVM 设置。这提高了 SnapStart 和预置并发的性能，同时又不会产生冷启动的负面影响，因为在这些情况下，分层编译是在调用路径之外进行的。为了充分发挥这一优势，您可以在函数初始化过程中使用引导 - 执行代码路径，以便在拍摄 SnapStart 快照之前或在预置并发执行环境被预先配置时触发 JIT。有关更多信息，请参阅博客文章[利用 SnapStart 结合先进的引导策略来优化 AWS Lambda 的冷启动性能](https://aws.amazon.com/blogs/compute/optimizing-cold-start-performance-of-aws-lambda-using-advanced-priming-strategies-with-snapstart/)。

**自定义分层编译设置（控制台）**

1. 在 Lambda 控制台中打开[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择要为其自定义分层编译的 Java 函数。

1. 选择**配置**选项卡，然后从左侧菜单中选择**环境变量**。

1. 选择**编辑**。

1. 选择**添加环境变量**。

1.  对于键，输入 `JAVA_TOOL_OPTIONS`。对于值，输入 `-XX:+TieredCompilation -XX:TieredStopAtLevel=1`。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/java-tool-options-tiered-compilation.png)

1. 选择**保存**。

**注意**  
您也可以使用 Lambda SnapStart 来缓解冷启动问题。SnapStart 使用执行环境的缓存快照来显著提高启动性能。有关 SnapStart 的功能、限制和支持的区域的更多信息，请参阅 [使用 Lambda SnapStart 提高启动性能](snapstart.md)。

### 示例：使用 JAVA\$1TOOL\$1OPIONS 自定义 GC 行为
<a name="gc-behavior"></a>

Java 11 运行时系统使用[串行](https://docs.oracle.com/en/java/javase/18/gctuning/available-collectors.html#GUID-45794DA6-AB96-4856-A96D-FDE5F7DEE498)垃圾收集器（GC）实现垃圾回收。默认情况下，Java 17 运行时系统也使用串行 GC。不过，在 Java 17 中，您也可以使用 `JAVA_TOOL_OPTIONS` 环境变量来更改默认 GC。您可以在 Parallel GC 和 [Shenandoah GC](https://wiki.openjdk.org/display/shenandoah/Main) 之间进行选择。

例如，如果工作负载会使用更多内存和多个 CPU，则考虑使用 Parallel GC 来获得更好的性能。为此，您可以将以下内容附加到 `JAVA_TOOL_OPTIONS` 环境变量的值中：

```
-XX:+UseParallelGC
```

如果您的工作负载中包含大量临时对象，则通过启用 Java 25 中引入的 Shenandoah 垃圾回收器代际模型，您可能会降低内存消耗从而获得益处。为此，可以将以下内容附加到 `JAVA_TOOL_OPTIONS` 环境变量的值中：

```
-XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational
```

## Log4Shell 的 Log4j 补丁
<a name="log4shell-patch"></a>

适用于 Java 8、11、17 和 21 的 Lambda 运行时包括 Log4j（一款广受欢迎的 Java 日志记录框架）中的 Log4Shell 漏洞（CVE-2021-44228）的修复补丁。此修复补丁会产生冷启动性能开销。如果您使用的是 Log4j 的补丁版本（版本 2.17.0 或更高版本），则可以禁用此补丁以提高冷启动性能。要禁止此补丁，请将环境变量 `AWS_LAMBDA_DISABLE_CVE_2021_44228_PROTECTION` 设置为 `true`。

从 Java 25 版本开始，Lambda 运行时不再包含 Log4Shell 补丁。您必须确认您使用的是 Log4j 版本 2.17.0 或更高版本。

## 提前编译（AOT）和 CDS 缓存
<a name="aot-cds-caches"></a>

从 Java 25 版本开始，Lambda 运行时为 Java 运行时接口客户端（RIC）配备了提前编译（AOT）缓存。RIC 是一个会主动从 Lambda 运行时 API 轮询事件的运行时组件。这可以提高冷启动性能。

AOT 缓存特定于 JVM 版本。当 Lambda 更新托管运行时时，它还会更新 RIC 的 AOT 缓存。但是，如果您部署自己的 AOT 缓存，则在运行时更新后，这些缓存可能会失效，或者导致出现意外情况。因此，我们强烈建议在使用托管运行时时不要使用 AOT 缓存。要使用 AOT 缓存，您需要使用容器映像来部署您的函数。

AOT 缓存不能与类数据共享（CDS）缓存一起使用。如果您在 Lambda 函数中部署了 CDS 缓存，则 Lambda 会禁用 AOT 缓存。

# 使用 Lambda 上下文对象检索 Java 函数信息
<a name="java-context"></a>

当 Lambda 运行您的函数时，它会将上下文对象传递到[处理程序](java-handler.md)。此对象提供的方法和属性包含有关调用、函数和执行环境的信息。

**上下文方法**
+ `getRemainingTimeInMillis()` – 返回执行超时前剩余的毫秒数。
+ `getFunctionName()` – 返回 Lambda 函数的名称。
+ `getFunctionVersion()` – 返回函数的[版本](configuration-versions.md)。
+ `getInvokedFunctionArn()` – 返回用于调用函数的 Amazon 资源名称（ARN）。表明调用者是否指定了版本号或别名。
+ `getMemoryLimitInMB()` – 返回为函数分配的内存量。
+ `getAwsRequestId()` – 返回调用请求的标识符。
+ `getLogGroupName()` – 返回函数的日志组。
+ `getLogStreamName()` – 返回函数实例的日志流。
+ `getIdentity()` –（移动应用程序）授权请求的 Amazon Cognito 身份的相关信息。
+ `getClientContext()` –（移动应用程序）返回客户端应用程序提供给 Lambda 的客户端上下文。
+ `getLogger()` – 返回函数的[记录器](java-logging.md)对象。

以下示例显示一个使用上下文对象访问 Lambda 记录器的函数。

**Example [Handler.java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic/src/main/java/example/Handler.java)**  

```
package example;

import [com.amazonaws.services.lambda.runtime.Context](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/Context.java);
import [com.amazonaws.services.lambda.runtime.LambdaLogger](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/LambdaLogger.java);
import [com.amazonaws.services.lambda.runtime.RequestHandler](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/RequestHandler.java);

import java.util.Map;

// Handler value: example.Handler
public class Handler implements RequestHandler<Map<String,String>, Void>{

  @Override
  public Void handleRequest(Map<String,String> event, Context context)
  {
    LambdaLogger logger = context.getLogger();
    logger.log("EVENT TYPE: " + event.getClass());
    return null;
  }
}
```

该函数在返回 `null` 之前记录传入事件的类类型。

**Example 日志输出**  

```
EVENT TYPE: class java.util.LinkedHashMap
```

上下文对象的接口在 [aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-core) 库中可用。您可以实现此接口来创建用于测试的上下文类。以下示例显示一个上下文类，该类返回大多数属性的虚拟值和一个有效的测试记录器。

**Example [src/test/java/example/TestContext.java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic/src/test/java/example/TestContext.java)**  

```
package example;

import [com.amazonaws.services.lambda.runtime.Context](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/Context.java);
import [com.amazonaws.services.lambda.runtime.CognitoIdentity](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/CognitoIdentity.java);
import [com.amazonaws.services.lambda.runtime.ClientContext](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/ClientContext.java);
import [com.amazonaws.services.lambda.runtime.LambdaLogger](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-core/src/main/java/com/amazonaws/services/lambda/runtime/LambdaLogger.java);

public class TestContext implements Context{

  public TestContext() {}
  public String getAwsRequestId(){
    return new String("495b12a8-xmpl-4eca-8168-160484189f99");
  }
  public String getLogGroupName(){
    return new String("/aws/lambda/my-function");
  }
  public String getLogStreamName(){
    return new String("2020/02/26/[$LATEST]704f8dxmpla04097b9134246b8438f1a");
  }
  public String getFunctionName(){
    return new String("my-function");
  }
  public String getFunctionVersion(){
    return new String("$LATEST");
  }
  public String getInvokedFunctionArn(){
    return new String("arn:aws:lambda:us-east-2:123456789012:function:my-function");
  }
  public CognitoIdentity getIdentity(){
    return null;
  }
  public ClientContext getClientContext(){
    return null;
  }
  public int getRemainingTimeInMillis(){
    return 300000;
  }
  public int getMemoryLimitInMB(){
    return 512;
  }
  public LambdaLogger getLogger(){
    return new TestLogger();
  }

}
```

有关日志记录的更多信息，请参阅[Java Lambda 函数日志记录和监控](java-logging.md)。

## 示例应用程序中的上下文
<a name="java-context-samples"></a>

本指南的 GitHub 存储库包括演示如何使用上下文对象的示例应用程序。每个示例应用程序都包含用于轻松部署和清理的脚本、一个 AWS Serverless Application Model (AWS SAM) 模板和支持资源。

**Java 中的 Lambda 应用程序示例**
+ [example-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/example-java) – 一个 Java 函数，演示如何使用 Lambda 来处理顺序。此函数说明了如何定义和反序列化自定义输入事件对象，如何使用 AWS SDK 和输出日志记录。
+ [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) – 具有单元测试和变量日志记录配置的最小 Java 函数的集合。
+ [java-events](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events) – Java 函数的集合，其中包含用于处理来自 Amazon API Gateway、Amazon SQS 和 Amazon Kinesis 等各种服务的事件的框架代码。这些函数使用最新版本的 [aws-lambda-events](java-package.md) 库（3.0.0 及更新版本）。这些示例不需要 AWS 开发工具包作为依赖项。
+ [s3-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java) – 此 Java 函数可处理来自 Amazon S3 的通知事件，并使用 Java 类库（JCL）从上传的图像文件创建缩略图。
+ [layer-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-java) – 一个 Java 函数，说明如何使用 Lambda 层将依赖项与核心函数代码分开打包。

# Java Lambda 函数日志记录和监控
<a name="java-logging"></a>

AWS Lambda 将自动监控 Lambda 函数并将日志条目发送到 Amazon CloudWatch。您的 Lambda 函数带有一个 CloudWatch Logs 日志组以及函数的每个实例的日志流。Lambda 运行时系统环境会将每次调用的详细信息以及函数代码的其他输出发送到该日志流。有关 CloudWatch Logs 的更多信息，请参阅[将 Lambda 函数日志发送到 CloudWatch Logs](monitoring-cloudwatchlogs.md)。

要从函数代码输出日志，您可以使用 [java.lang.System](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html) 的方法，或使用写入到 stdout 或 stderr 的任何日志记录模块。

**Topics**
+ [创建返回日志的函数](#java-logging-output)
+ [在 Java 中使用 Lambda 高级日志记录控件](#java-logging-advanced)
+ [使用 Log4j2 和 SLF4J 实现高级日志记录](#java-logging-log4j2)
+ [使用其他日志记录工具和库](#java-tools-libraries)
+ [将 Powertools for AWS Lambda（Java）和 AWS SAM 用于结构化日志记录](#java-logging-sam)
+ [在 Lambda 控制台中查看日志](#java-logging-console)
+ [在 CloudWatch 控制台中查看日志](#java-logging-cwconsole)
+ [使用 AWS Command Line Interface（AWS CLI）查看日志](#java-logging-cli)
+ [删除日志](#java-logging-delete)
+ [日志记录代码示例](#java-logging-samples)

## 创建返回日志的函数
<a name="java-logging-output"></a>

要从函数代码输出日志，您可以使用 [java.lang.System](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html) 的方法，或使用写入到 `stdout` 或 `stderr` 的任何日志记录模块。[aws-lambda-java-core](java-package.md) 库提供一个名为 `LambdaLogger` 的记录器类，您可以从上下文对象访问该类。记录器类支持多行日志。

以下示例使用上下文对象提供的 `LambdaLogger` 记录器。

**Example Handler.java**  

```
// Handler value: example.Handler
public class Handler implements RequestHandler<Object, String>{
  Gson gson = new GsonBuilder().setPrettyPrinting().create();
  @Override
  public String handleRequest(Object event, Context context)
  {
    LambdaLogger logger = context.getLogger();
    String response = new String("SUCCESS");
    // log execution details
    logger.log("ENVIRONMENT VARIABLES: " + gson.toJson(System.getenv()));
    logger.log("CONTEXT: " + gson.toJson(context));
    // process event
    logger.log("EVENT: " + gson.toJson(event));
    return response;
  }
}
```

**Example 日志格式**  

```
START RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 Version: $LATEST
ENVIRONMENT VARIABLES: 
{
    "_HANDLER": "example.Handler",
    "AWS_EXECUTION_ENV": "AWS_Lambda_java8",
    "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512",
    ...
}
CONTEXT: 
{
    "memoryLimit": 512,
    "awsRequestId": "6bc28136-xmpl-4365-b021-0ce6b2e64ab0",
    "functionName": "java-console",
    ...
}
EVENT:
{
  "records": [
    {
      "messageId": "19dd0b57-xmpl-4ac1-bd88-01bbb068cb78",
      "receiptHandle": "MessageReceiptHandle",
      "body": "Hello from SQS!",
       ...
    }
  ]
}
END RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0
REPORT RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0	Duration: 198.50 ms	Billed Duration: 724 ms	Memory Size: 512 MB	Max Memory Used: 90 MB	Init Duration: 524.75 ms
```

Java 运行时记录每次调用的 `START`、`END` 和 `REPORT` 行。报告行提供了以下详细信息：

**REPORT 行数据字段**
+ **RequestId** – 调用的唯一请求 ID。
+ **Duration**（持续时间）– 函数的处理程序方法处理事件所花费的时间。
+ **Billed Duration**（计费持续时间）– 针对调用计费的时间量。
+ **Memory Size**（内存大小）– 分配给函数的内存量。
+ **Max Memory Used**（最大内存使用量）– 函数使用的内存量。如果调用共享执行环境，Lambda 会报告所有调用使用的最大内存。此行为可能会导致报告值高于预期。
+ **Init Duration**（初始持续时间）– 对于提供的第一个请求，为运行时在处理程序方法外部加载函数和运行代码所花费的时间。
+ **XRAY TraceId** – 对于追踪的请求，为 [AWS X-Ray 追踪 ID](services-xray.md)。
+ **SegmentId** – 对于追踪的请求，为 X-Ray 分段 ID。
+ **Sampled**（采样）– 对于追踪的请求，为采样结果。

## 在 Java 中使用 Lambda 高级日志记录控件
<a name="java-logging-advanced"></a>

为了更好地控制捕获、处理和使用函数日志的方式，您可以为支持的 Java 运行时系统配置以下日志记录选项：
+ **日志格式** - 为函数日志选择纯文本或结构化的 JSON 格式
+ **日志级别** - 对于 JSON 格式的日志，选择 Lambda 发送到 CloudWatch 的日志的详细信息级别，例如 ERROR、DEBUG 或 INFO
+ **日志组** - 选择您的函数发送日志的目标 CloudWatch 日志组

有关这些日志记录选项的更多信息以及如何通过配置来使用函数的说明，请参阅 [为 Lambda 函数配置高级日志记录控件](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)。

要在 Java Lambda 函数中使用日志格式和日志级别选项，请参阅以下各节中的指南。

### 在 Java 中使用结构化的 JSON 日志格式
<a name="java-logging-advanced-JSON"></a>

如果您为函数的日志格式选择 JSON，Lambda 将使用 `LambdaLogger` 类将日志输出作为结构化的 JSON 发送到 CloudWatch。每个 JSON 日志对象包含至少四个键值对和以下键：
+ `"timestamp"` - 生成日志消息的时间
+ `"level"` - 分配给消息的日志级别
+ `"message"` - 日志消息的内容
+ `"AWSrequestId"` - 函数调用的唯一请求 ID

根据您使用的日志记录方法，以 JSON 格式捕获的函数日志输出还可能包含其他键值对。

要为使用 `LambdaLogger` 记录器创建的日志分配级别，您需要在日志记录命令中提供 `LogLevel` 参数，如以下示例所示。

**Example Java 日志记录代码**  

```
LambdaLogger logger = context.getLogger();
logger.log("This is a debug log", LogLevel.DEBUG);
```

此示例代码输出的日志将在 CloudWatch Logs 中被捕获，如下所示：

**Example JSON 日志记录**  

```
{
    "timestamp":"2023-11-01T00:21:51.358Z",
    "level":"DEBUG",
    "message":"This is a debug log",
    "AWSrequestId":"93f25699-2cbf-4976-8f94-336a0aa98c6f"
}
```

如果您没有为日志输出分配级别，Lambda 将自动为其分配 INFO 级别。

如果您的代码已经使用其他日志记录库来生成 JSON 结构化日志，则无需进行任何更改。Lambda 不会对任何已采用 JSON 编码的日志进行双重编码。即使您将函数配置为使用 JSON 日志格式，您的日志输出也会以您定义的 JSON 结构显示在 CloudWatch 中。

### 在 Java 中使用日志级别筛选
<a name="java-logging-advanced-levels"></a>

为了让 AWS Lambda 根据日志级别筛选应用程序日志，您的函数必须使用 JSON 格式的日志。您可以通过两种方式实现这一点：
+ 使用标准 `LambdaLogger` 创建日志输出，并将您的函数配置为使用 JSON 日志格式。然后，Lambda 使用 [在 Java 中使用结构化的 JSON 日志格式](#java-logging-advanced-JSON) 中所述的 JSON 对象中的“级别”键值对筛选您的日志输出。要了解如何配置函数的日志格式，请参阅 [为 Lambda 函数配置高级日志记录控件](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)。
+ 使用其他日志记录库或方法在代码中创建 JSON 结构化日志，其中包含定义日志输出级别的“级别”键值对。您可以使用任何可将 JSON 日志写入 `stdout` 或 `stderr` 的日志记录库。例如，您可以使用 Powertools for AWS Lambda 或 Log4j2 软件包通过代码生成 JSON 结构化日志输出。要了解更多信息，请参阅 [将 Powertools for AWS Lambda（Java）和 AWS SAM 用于结构化日志记录](#java-logging-sam) 和 [使用 Log4j2 和 SLF4J 实现高级日志记录](#java-logging-log4j2)。

将函数配置为使用日志级别筛选时，您必须从以下选项中选择希望 Lambda 发送到 CloudWatch Logs 的日志级别：


| 日志级别 | 标准使用情况 | 
| --- | --- | 
| TRACE（最详细） | 用于跟踪代码执行路径的最精细信息 | 
| 调试 | 系统调试的详细信息 | 
| INFO | 记录函数正常运行情况的消息 | 
| 警告 | 有关潜在错误的消息，如果不加以解决，这些错误可能会导致意外行为 | 
| ERROR | 有关会阻碍代码按预期执行的问题的消息 | 
| FATAL（最简略） | 有关导致应用程序停止运行的严重错误的消息 | 

要让 Lambda 筛选函数的日志，还必须在 JSON 日志输出中包含一个 `"timestamp"` 键值对。必须以有效的 [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) 时间戳格式指定时间。如果您未提供有效的时间戳，Lambda 将为日志分配 INFO 级别并为您添加时间戳。

Lambda 仅将选定级别及更低级别的系统日志发送到 CloudWatch。例如，如果您将日志级别配置为 WARN，Lambda 将发送与 WARN、ERROR 和 FATAL 级别相对应的日志。

## 使用 Log4j2 和 SLF4J 实现高级日志记录
<a name="java-logging-log4j2"></a>

**注意**  
 AWS Lambda 的托管式运行时或基本容器映像中不包括 Log4j2。因此，这些不受 CVE-2021-44228、CVE-2021-45046 和 CVE-2021-45105 中描述的问题影响。  
 对于客户的函数包含受影响的 Log4j2 版本的情况，我们对 Lambda Java [托管式运行时](lambda-runtimes.md)和[基本容器映像](java-image.md)应用了更改，以帮助缓解 CVE-2021-44228、CVE-2021-45046 和 CVE-2021-45105 中描述的问题。由于这一更改，使用 Log4J2 的客户可能会看到额外的日志条目，类似于“`Transforming org/apache/logging/log4j/core/lookup/JndiLookup (java.net.URLClassLoader@...)`”。Log4J2 输出中引用 jndi 映射器的任何日志字符串都将替换为“`Patched JndiLookup::lookup()`”。  
 无论是否有这一更改，我们强烈建议其函数中包括 Log4j2 的所有客户更新到最新版本。具体来说，在函数中使用 aws-lambda-java-log4j2 库的客户应更新到版本 1.5.0（或更高版本），然后重新部署函数。此版本将底层 Log4j2 实用程序依赖项更新为版本 2.17.0（或更高版本）。更新后的 aws-lambda-java-log4j2 二进制文件可在 [Maven 存储库](https://repo1.maven.org/maven2/com/amazonaws/aws-lambda-java-log4j2/)中获取，其源代码可在 [Github](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-log4j2) 中获取。  
 最后，请注意，在**任何**情况下都**不**应使用任何与 **aws-lambda-java-log4j（v1.0.0 或 1.0.1**）相关的库。这些库与 log4j 的 1.x 版本相关，该版本已于 2015 年终止使用。这些库不受支持、未维护、未修补，并且存在已知的安全漏洞。

要自定义日志输出、在单元测试期间支持日志记录以及记录 AWS 开发工具包调用，请将 Apache Log4j2 与 SLF4J 结合使用。Log4j 是 Java 程序的日志库，这些程序使您能够配置日志级别和使用 Appender 库。SLF4J 是一个 Facade 库，可让您更改您使用的库，而不更改函数代码。

要将请求 ID 添加到函数的日志中，请使用 [aws-lambda-java-log4j2](java-package.md) 库中的 Appender。

**Example [src/main/resources/log4j2.xml](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java/src/main/resources/log4j2.xml) – Appender 配置**  

```
<Configuration>
  <Appenders>
    <Lambda name="Lambda" format="${env:AWS_LAMBDA_LOG_FORMAT:-TEXT}">
       <LambdaTextFormat>
         <PatternLayout>
             <pattern>%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1} - %m%n </pattern>
         </PatternLayout>
       </LambdaTextFormat>
       <LambdaJSONFormat>
         <JsonTemplateLayout eventTemplateUri="classpath:LambdaLayout.json" />
       </LambdaJSONFormat>
     </Lambda>
   </Appenders>
   <Loggers>
     <Root level="${env:AWS_LAMBDA_LOG_LEVEL:-INFO}">
       <AppenderRef ref="Lambda"/>
     </Root>
     <Logger name="software.amazon.awssdk" level="WARN" />
     <Logger name="software.amazon.awssdk.request" level="DEBUG" />
   </Loggers>
 </Configuration>
```

您可以通过在 `<LambdaTextFormat>` 和 `<LambdaJSONFormat>` 标签下指定布局来决定如何为纯文本或 JSON 输出配置 Log4j2 日志。

在此示例中，使用文本模式，每行都会在前面加上日期、时间、请求 ID、日志级别和类名。在 JSON 模式下，`<JsonTemplateLayout>` 要结合与 `aws-lambda-java-log4j2` 库一起提供的配置使用。

SLF4J 是一个用于在 Java 代码中进行日志记录的 Facade 库。在函数代码中，您可以使用 SLF4J 记录器工厂，通过适用于日志级别（如 `info()` 和 `warn()`）的方法来检索记录器。在构建配置中，您可以在类路径中包含日志记录库和 SLF4J 适配器。通过更改构建配置中的库，您可以在不更改函数代码的情况下更改记录器类型。从适用于 Java 的开发工具包中捕获日志需要使用 SLF4J。

在以下示例代码中，处理程序类使用 SLF4J 检索记录器。

**Example [src/main/java/example/HandlerS3.java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events/src/main/java/example/HandlerS3.java)：使用 SLF4J 进行日志记录**  

```
package example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;

import static org.apache.logging.log4j.CloseableThreadContext.put;


public class HandlerS3 implements RequestHandler<S3Event, String>{
    private static final Logger logger = LoggerFactory.getLogger(HandlerS3.class);

    @Override
    public String handleRequest(S3Event event, Context context) {
        for(var record : event.getRecords()) {
            try (var loggingCtx = put("awsRegion", record.getAwsRegion())) {
                loggingCtx.put("eventName", record.getEventName());
                loggingCtx.put("bucket", record.getS3().getBucket().getName());
                loggingCtx.put("key", record.getS3().getObject().getKey());

                logger.info("Handling s3 event");
            }
        }

        return "Ok";
    }
}
```

此代码产生类似下面的日志输出：

**Example 日志格式**  

```
{
    "timestamp": "2023-11-15T16:56:00.815Z",
    "level": "INFO",
    "message": "Handling s3 event",
    "logger": "example.HandlerS3",
    "AWSRequestId": "0bced576-3936-4e5a-9dcd-db9477b77f97",
    "awsRegion": "eu-south-1",
    "bucket": "java-logging-test-input-bucket",
    "eventName": "ObjectCreated:Put",
    "key": "test-folder/"
}
```

构建配置使用 Lambda Appender 和 SLF4J 适配器上的运行时系统依赖项以及 Log4j2 上的实现依赖项。

**Example build.gradle – 日志记录依赖项**  

```
dependencies {
    ...
    'com.amazonaws:aws-lambda-java-log4j2:[1.6.0,)',
    'com.amazonaws:aws-lambda-java-events:[3.11.3,)',
    'org.apache.logging.log4j:log4j-layout-template-json:[2.17.1,)',
    'org.apache.logging.log4j:log4j-slf4j2-impl:[2.19.0,)',
    ...
}
```

在本地运行代码进行测试时，带有 Lambda 记录器的上下文对象不可用，并且没有供 Lambda Appender 使用的请求 ID 。有关测试配置示例，请参阅下一节中的示例应用程序。

## 使用其他日志记录工具和库
<a name="java-tools-libraries"></a>

[Powertools for AWS Lambda（Java）](https://docs.aws.amazon.com/powertools/java/)是一个开发人员工具包，用于实施无服务器最佳实践并提高开发人员速度。[日志记录实用程序](https://docs.aws.amazon.com/powertools/java/latest/core/logging/)提供经优化的 Lambda 日志记录程序，其中包含有关所有函数的函数上下文的附加信息，输出结构为 JSON。请使用该实用程序执行以下操作：
+ 从 Lambda 上下文中捕获关键字段，冷启动并将日志记录输出结构化为 JSON
+ 根据指示记录 Lambda 调用事件（默认情况下禁用）
+ 通过日志采样仅针对一定百分比的调用输出所有日志（默认情况下禁用）
+ 在任何时间点将其他键附加到结构化日志
+ 使用自定义日志格式设置程序（自带格式设置程序），从而在与组织的日志记录 RFC 兼容的结构中输出日志

## 将 Powertools for AWS Lambda（Java）和 AWS SAM 用于结构化日志记录
<a name="java-logging-sam"></a>

请按照以下步骤使用 AWS SAM，通过集成的 [Powertools for AWS Lambda（Java）](https://docs.aws.amazon.com/powertools/java/latest/)模块来下载、构建和部署示例 Hello World Java 应用程序。此应用程序实现了基本的 API 后端，并使用 Powertools 发送日志、指标和跟踪。它由 Amazon API Gateway 端点和 Lambda 函数组成。在向 API Gateway 端点发送 GET 请求时，Lambda 函数会使用嵌入式指标格式向 CloudWatch 调用、发送日志和指标，并向 AWS X-Ray 发送跟踪。该函数将返回一条 `hello world` 消息。

**先决条件**

要完成本节中的步骤，您必须满足以下条件：
+ Java 11
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [AWS SAM CLI 版本 1.75 或更高版本](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)。如果您使用的是旧版本的 AWS SAM CLI，请参阅[升级 AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/manage-sam-cli-versions.html#manage-sam-cli-versions-upgrade)。

**部署示例 AWS SAM 应用程序**

1. 使用 Hello World Java 模板初始化该应用程序。

   ```
   sam init --app-template hello-world-powertools-java --name sam-app --package-type Zip --runtime java11 --no-tracing
   ```

1. 构建应用程序。

   ```
   cd sam-app && sam build
   ```

1. 部署应用程序。

   ```
   sam deploy --guided
   ```

1. 按照屏幕上的提示操作。要在交互式体验中接受提供的默认选项，请按 `Enter`。
**注意**  
对于 **HelloWorldFunction 可能没有定义授权，确定执行此操作吗？**，确保输入 `y`。

1. 获取已部署应用程序的 URL：

   ```
   aws cloudformation describe-stacks --stack-name sam-app --query 'Stacks[0].Outputs[?OutputKey==`HelloWorldApi`].OutputValue' --output text
   ```

1. 调用 API 端点：

   ```
   curl -X GET <URL_FROM_PREVIOUS_STEP>
   ```

   如果成功，您将会看到如下响应：

   ```
   {"message":"hello world"}
   ```

1. 要获取该函数的日志，请运行 [sam logs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-logs.html)。有关更多信息，请参阅《AWS Serverless Application Model 开发人员指南》中的 [使用日志](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html)**。

   ```
   sam logs --stack-name sam-app
   ```

   该日志输出类似于以下示例：

   ```
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:34.095000 INIT_START Runtime Version: java:11.v15    Runtime Version ARN: arn:aws:lambda:eu-central-1::runtime:0a25e3e7a1cc9ce404bc435eeb2ad358d8fa64338e618d0c224fe509403583ca
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:34.114000 Picked up JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:34.793000 Transforming org/apache/logging/log4j/core/lookup/JndiLookup (lambdainternal.CustomerClassLoader@1a6c5a9e)
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:35.252000 START RequestId: 7fcf1548-d2d4-41cd-a9a8-6ae47c51f765 Version: $LATEST
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:36.531000 {
     "_aws": {
       "Timestamp": 1675416276051,
       "CloudWatchMetrics": [
         {
           "Namespace": "sam-app-powerools-java",
           "Metrics": [
             {
               "Name": "ColdStart",
               "Unit": "Count"
             }
           ],
           "Dimensions": [
             [
               "Service",
               "FunctionName"
             ]
           ]
         }
       ]
     },
     "function_request_id": "7fcf1548-d2d4-41cd-a9a8-6ae47c51f765",
     "traceId": "Root=1-63dcd2d1-25f90b9d1c753a783547f4dd;Parent=e29684c1be352ce4;Sampled=1",
     "FunctionName": "sam-app-HelloWorldFunction-y9Iu1FLJJBGD",
     "functionVersion": "$LATEST",
     "ColdStart": 1.0,
     "Service": "service_undefined",
     "logStreamId": "2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81",
     "executionEnvironment": "AWS_Lambda_java11"
   }
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:36.974000 Feb 03, 2023 9:24:36 AM com.amazonaws.xray.AWSXRayRecorder <init>
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:36.993000 Feb 03, 2023 9:24:36 AM com.amazonaws.xray.config.DaemonConfiguration <init>
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:36.993000 INFO: Environment variable AWS_XRAY_DAEMON_ADDRESS is set. Emitting to daemon on address XXXX.XXXX.XXXX.XXXX:2000.
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:37.331000 09:24:37.294 [main] INFO  helloworld.App - {"version":null,"resource":"/hello","path":"/hello/","httpMethod":"GET","headers":{"Accept":"*/*","CloudFront-Forwarded-Proto":"https","CloudFront-Is-Desktop-Viewer":"true","CloudFront-Is-Mobile-Viewer":"false","CloudFront-Is-SmartTV-Viewer":"false","CloudFront-Is-Tablet-Viewer":"false","CloudFront-Viewer-ASN":"16509","CloudFront-Viewer-Country":"IE","Host":"XXXX.execute-api.eu-central-1.amazonaws.com","User-Agent":"curl/7.86.0","Via":"2.0 f0300a9921a99446a44423d996042050.cloudfront.net (CloudFront)","X-Amz-Cf-Id":"t9W5ByT11HaY33NM8YioKECn_4eMpNsOMPfEVRczD7T1RdhbtiwV1Q==","X-Amzn-Trace-Id":"Root=1-63dcd2d1-25f90b9d1c753a783547f4dd","X-Forwarded-For":"XX.XXX.XXX.XX, XX.XXX.XXX.XX","X-Forwarded-Port":"443","X-Forwarded-Proto":"https"},"multiValueHeaders":{"Accept":["*/*"],"CloudFront-Forwarded-Proto":["https"],"CloudFront-Is-Desktop-Viewer":["true"],"CloudFront-Is-Mobile-Viewer":["false"],"CloudFront-Is-SmartTV-Viewer":["false"],"CloudFront-Is-Tablet-Viewer":["false"],"CloudFront-Viewer-ASN":["16509"],"CloudFront-Viewer-Country":["IE"],"Host":["XXXX.execute-api.eu-central-1.amazonaws.com"],"User-Agent":["curl/7.86.0"],"Via":["2.0 f0300a9921a99446a44423d996042050.cloudfront.net (CloudFront)"],"X-Amz-Cf-Id":["t9W5ByT11HaY33NM8YioKECn_4eMpNsOMPfEVRczD7T1RdhbtiwV1Q=="],"X-Amzn-Trace-Id":["Root=1-63dcd2d1-25f90b9d1c753a783547f4dd"],"X-Forwarded-For":["XXX, XXX"],"X-Forwarded-Port":["443"],"X-Forwarded-Proto":["https"]},"queryStringParameters":null,"multiValueQueryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"accountId":"XXX","stage":"Prod","resourceId":"at73a1","requestId":"ba09ecd2-acf3-40f6-89af-fad32df67597","operationName":null,"identity":{"cognitoIdentityPoolId":null,"accountId":null,"cognitoIdentityId":null,"caller":null,"apiKey":null,"principalOrgId":null,"sourceIp":"54.240.197.236","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":null,"userAgent":"curl/7.86.0","user":null,"accessKey":null},"resourcePath":"/hello","httpMethod":"GET","apiId":"XXX","path":"/Prod/hello/","authorizer":null},"body":null,"isBase64Encoded":false}
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:37.351000 09:24:37.351 [main] INFO  helloworld.App - Retrieving https://checkip.amazonaws.com
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:39.313000 {
     "function_request_id": "7fcf1548-d2d4-41cd-a9a8-6ae47c51f765",
     "traceId": "Root=1-63dcd2d1-25f90b9d1c753a783547f4dd;Parent=e29684c1be352ce4;Sampled=1",
     "xray_trace_id": "1-63dcd2d1-25f90b9d1c753a783547f4dd",
     "functionVersion": "$LATEST",
     "Service": "service_undefined",
     "logStreamId": "2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81",
     "executionEnvironment": "AWS_Lambda_java11"
   }
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:39.371000 END RequestId: 7fcf1548-d2d4-41cd-a9a8-6ae47c51f765
   2025/09/03/[$LATEST]851411a899b545eea2cffeba4cfbec81 2023-02-03T09:24:39.371000 REPORT RequestId: 7fcf1548-d2d4-41cd-a9a8-6ae47c51f765    Duration: 4118.98 ms    Billed Duration: 5275 ms    Memory Size: 512 MB    Max Memory Used: 152 MB    Init Duration: 1155.47 ms    
   XRAY TraceId: 1-63dcd2d1-25f90b9d1c753a783547f4dd    SegmentId: 3a028fee19b895cb    Sampled: true
   ```

1. 这是一个可以通过互联网访问的公有 API 端点。我们建议您在测试后删除该端点。

   ```
   sam delete
   ```

### 管理日志保留日期
<a name="java-log-retention"></a>

删除函数时，日志组不会自动删除。要避免无限期存储日志，请删除日志组，或配置一个保留期，在该保留期结束后，日志将自动删除。要设置日志保留日期，请将以下内容添加到您的 AWS SAM 模板中：

```
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      # Omitting other properties

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${HelloWorldFunction}"
      RetentionInDays: 7
```

## 在 Lambda 控制台中查看日志
<a name="java-logging-console"></a>

调用 Lambda 函数后，您可以使用 Lambda 控制台查看日志输出。

如果可以在嵌入式**代码**编辑器中测试代码，则可以在**执行结果**中找到日志。使用控制台测试功能调用函数时，可以在**详细信息**部分找到**日志输出**。

## 在 CloudWatch 控制台中查看日志
<a name="java-logging-cwconsole"></a>

您可以使用 Amazon CloudWatch 控制台查看所有 Lambda 函数调用的日志。

**使用 CloudWatch 控制台查看日志**

1. 打开 CloudWatch 控制台的 [Log groups](https://console.aws.amazon.com/cloudwatch/home?#logs:)（日志组页面）。

1. 选择您的函数 (**/aws/lambda/*your-function-name***) 的日志组。

1. 创建日志流。

每个日志流对应一个[函数实例](lambda-runtime-environment.md)。日志流会在您更新 Lambda 函数以及创建更多实例来处理并发调用时显示。要查找特定调用的日志，建议您使用 AWS X-Ray 检测函数。X-Ray 会在追踪中记录有关请求和日志流的详细信息。

## 使用 AWS Command Line Interface（AWS CLI）查看日志
<a name="java-logging-cli"></a>

AWS CLI 是一种开源工具，让您能够在命令行 Shell 中使用命令与 AWS 服务进行交互。要完成本节中的步骤，您必须拥有 [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

您可以通过 [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)，使用 `--log-type` 命令选项检索调用的日志。响应包含一个 `LogResult` 字段，其中包含多达 4KB 来自调用的 base64 编码日志。

**Example 检索日志 ID**  
以下示例说明如何从 `LogResult` 字段中检索名为 `my-function` 的函数的*日志 ID*。  

```
aws lambda invoke --function-name my-function out --log-type Tail
```
您应看到以下输出：  

```
{
    "StatusCode": 200,
    "LogResult": "U1RBUlQgUmVxdWVzdElkOiA4N2QwNDRiOC1mMTU0LTExZTgtOGNkYS0yOTc0YzVlNGZiMjEgVmVyc2lvb...",
    "ExecutedVersion": "$LATEST"
}
```

**Example 解码日志**  
在同一命令提示符下，使用 `base64` 实用程序解码日志。以下示例说明如何为 `my-function` 检索 base64 编码的日志。  

```
aws lambda invoke --function-name my-function out --log-type Tail \
--query 'LogResult' --output text --cli-binary-format raw-in-base64-out | base64 --decode
```
如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。  
您应看到以下输出：  

```
START RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8 Version: $LATEST
"AWS_SESSION_TOKEN": "AgoJb3JpZ2luX2VjELj...", "_X_AMZN_TRACE_ID": "Root=1-5d02e5ca-f5792818b6fe8368e5b51d50;Parent=191db58857df8395;Sampled=0"",ask/lib:/opt/lib",
END RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8
REPORT RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8  Duration: 79.67 ms      Billed Duration: 80 ms         Memory Size: 128 MB     Max Memory Used: 73 MB
```
`base64` 实用程序在 Linux、macOS 和 [Ubuntu on Windows](https://docs.microsoft.com/en-us/windows/wsl/install-win10) 上可用。macOS 用户可能需要使用 `base64 -D`。

**Example get-logs.sh 脚本**  
在同一命令提示符下，使用以下脚本下载最后五个日志事件。此脚本使用 `sed` 从输出文件中删除引号，并休眠 15 秒以等待日志可用。输出包括来自 Lambda 的响应，以及来自 `get-log-events` 命令的输出。  
复制以下代码示例的内容并将其作为 `get-logs.sh` 保存在 Lambda 项目目录中。  
如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。  

```
#!/bin/bash
aws lambda invoke --function-name my-function --cli-binary-format raw-in-base64-out --payload '{"key": "value"}' out
sed -i'' -e 's/"//g' out
sleep 15
aws logs get-log-events --log-group-name /aws/lambda/my-function --log-stream-name stream1 --limit 5
```

**Example macOS 和 Linux（仅限）**  
在同一命令提示符下，macOS 和 Linux 用户可能需要运行以下命令以确保脚本可执行。  

```
chmod -R 755 get-logs.sh
```

**Example 检索最后五个日志事件**  
在同一命令提示符下，运行以下脚本以获取最后五个日志事件。  

```
./get-logs.sh
```
您应看到以下输出：  

```
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
{
    "events": [
        {
            "timestamp": 1559763003171,
            "message": "START RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf Version: $LATEST\n",
            "ingestionTime": 1559763003309
        },
        {
            "timestamp": 1559763003173,
            "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tENVIRONMENT VARIABLES\r{\r  \"AWS_LAMBDA_FUNCTION_VERSION\": \"$LATEST\",\r ...",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003173,
            "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tEVENT\r{\r  \"key\": \"value\"\r}\n",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003218,
            "message": "END RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\n",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003218,
            "message": "REPORT RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\tDuration: 26.73 ms\tBilled Duration: 27 ms \tMemory Size: 128 MB\tMax Memory Used: 75 MB\t\n",
            "ingestionTime": 1559763018353
        }
    ],
    "nextForwardToken": "f/34783877304859518393868359594929986069206639495374241795",
    "nextBackwardToken": "b/34783877303811383369537420289090800615709599058929582080"
}
```

## 删除日志
<a name="java-logging-delete"></a>

删除函数时，日志组不会自动删除。要避免无限期存储日志，请删除日志组，或[配置一个保留期](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html#SettingLogRetention)，在该保留期之后，日志将自动删除。

## 日志记录代码示例
<a name="java-logging-samples"></a>

本指南的 GitHub 存储库包括演示如何使用各种日志记录配置的示例应用程序。每个示例应用程序都包含用于轻松部署和清理的脚本、一个 AWS SAM 模板和支持资源。

**Java 中的 Lambda 应用程序示例**
+ [example-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/example-java) – 一个 Java 函数，演示如何使用 Lambda 来处理顺序。此函数说明了如何定义和反序列化自定义输入事件对象，如何使用 AWS SDK 和输出日志记录。
+ [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) – 具有单元测试和变量日志记录配置的最小 Java 函数的集合。
+ [java-events](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events) – Java 函数的集合，其中包含用于处理来自 Amazon API Gateway、Amazon SQS 和 Amazon Kinesis 等各种服务的事件的框架代码。这些函数使用最新版本的 [aws-lambda-events](java-package.md) 库（3.0.0 及更新版本）。这些示例不需要 AWS 开发工具包作为依赖项。
+ [s3-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java) – 此 Java 函数可处理来自 Amazon S3 的通知事件，并使用 Java 类库（JCL）从上传的图像文件创建缩略图。
+ [layer-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-java) – 一个 Java 函数，说明如何使用 Lambda 层将依赖项与核心函数代码分开打包。

`java-basic` 示例应用程序显示支持日志记录测试的最小日志记录配置。处理程序代码使用上下文对象提供的 `LambdaLogger` 记录器。对于测试，应用程序使用一个自定义 `TestLogger` 类，此类实现带有 Log4j2 记录器的 `LambdaLogger` 接口。它使用 SLF4J 作为 Facade 以与AWS开发工具包兼容。从构建输出中排除日志记录库，以使部署程序包保持较小。

# 在 AWS Lambda 中检测 Java 代码
<a name="java-tracing"></a>

Lambda 与 AWS X-Ray 集成，以帮助您跟踪、调试和优化 Lambda 应用程序。您可以在某个请求遍历应用程序中的资源（其中可能包括 Lambda 函数和其他 AWS 服务）时，使用 X-Ray 跟踪该请求。

要将跟踪数据发送到 X-Ray，您可以使用以下两个软件开发工具包 (SDK) 库之一：
+ [适用于 OpenTelemetry 的 AWS 发行版 (ADOT)](https://aws.amazon.com/otel) – 一种安全、可供生产、支持 AWS 的 OpenTelemetry (OTel) SDK 的分发版本。
+ [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html) – 用于生成跟踪数据并将其发送到 X-Ray 的 SDK
+ [Powertools for AWS Lambda（Java）](https://docs.aws.amazon.com/powertools/java/latest/)– 一个开发人员工具包，用于实施无服务器最佳实践并提高开发人员速度。

每个开发工具包均提供了将遥测数据发送到 X-Ray 服务的方法。然后，您可以使用 X-Ray 查看、筛选和获得对应用程序性能指标的洞察，从而发现问题和优化机会。

**重要**  
X-Ray 和 Powertools for AWS Lambda SDK 是 AWS 提供的紧密集成的分析解决方案的一部分。ADOT Lambda Layers 是全行业通用的跟踪分析标准的一部分，该标准通常会收集更多数据，但可能不适用于所有使用案例。您可以使用任一解决方案在 X-Ray 中实现端到端跟踪。要了解有关如何在两者之间进行选择的更多信息，请参阅[在 AWS Distro for Open Telemetry 和 X-Ray 开发工具包之间进行选择](https://docs.aws.amazon.com/xray/latest/devguide/xray-instrumenting-your-app.html#xray-instrumenting-choosing)。

**Topics**
+ [将 Powertools for AWS Lambda（Java）和 AWS SAM 用于跟踪](#java-tracing-sam)
+ [将 Powertools for AWS Lambda（Java）和 AWS CDK 用于跟踪](#java-tracing-cdk)
+ [使用 ADOT 分析您的 Java 函数](#java-adot)
+ [使用 X-Ray SDK 分析您的 Java 函数](#java-xray-sdk)
+ [使用 Lambda 控制台激活跟踪](#java-tracing-console)
+ [使用 Lambda API 激活跟踪](#java-tracing-api)
+ [使用 CloudFormation 激活跟踪](#java-tracing-cloudformation)
+ [解释 X-Ray 跟踪](#java-tracing-interpretation)
+ [在层中存储运行时依赖项 (X-Ray SDK)](#java-tracing-layers)
+ [示例应用程序中的 X-Ray 跟踪 (X-Ray SDK)](#java-tracing-samples)

## 将 Powertools for AWS Lambda（Java）和 AWS SAM 用于跟踪
<a name="java-tracing-sam"></a>

请按照以下步骤使用 AWS SAM，通过集成的 [Powertools for AWS Lambda（Java）](https://docs.powertools.aws.dev/lambda-java)模块来下载、构建和部署示例 Hello World Java 应用程序。此应用程序实现了基本的 API 后端，并使用 Powertools 发送日志、指标和跟踪。它由 Amazon API Gateway 端点和 Lambda 函数组成。在向 API Gateway 端点发送 GET 请求时，Lambda 函数会使用嵌入式指标格式向 CloudWatch 调用、发送日志和指标，并向 AWS X-Ray 发送跟踪。该函数将返回一条 `hello world` 消息。

**先决条件**

要完成本节中的步骤，您必须满足以下条件：
+ Java 11 或更高版本
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [AWS SAM CLI 版本 1.75 或更高版本](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)。如果您使用的是旧版本的 AWS SAM CLI，请参阅[升级 AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/manage-sam-cli-versions.html#manage-sam-cli-versions-upgrade)。

**部署示例 AWS SAM 应用程序**

1. 使用 Hello World Java 模板初始化该应用程序。

   ```
   sam init --app-template hello-world-powertools-java --name sam-app --package-type Zip --runtime java11 --no-tracing
   ```

1. 构建应用程序。

   ```
   cd sam-app && sam build
   ```

1. 部署应用程序。

   ```
   sam deploy --guided
   ```

1. 按照屏幕上的提示操作。要在交互式体验中接受提供的默认选项，请按 `Enter`。
**注意**  
对于 **HelloWorldFunction 可能没有定义授权，确定执行此操作吗？**，确保输入 `y`。

1. 获取已部署应用程序的 URL：

   ```
   aws cloudformation describe-stacks --stack-name sam-app --query 'Stacks[0].Outputs[?OutputKey==`HelloWorldApi`].OutputValue' --output text
   ```

1. 调用 API 端点：

   ```
   curl -X GET <URL_FROM_PREVIOUS_STEP>
   ```

   如果成功，您将会看到如下响应：

   ```
   {"message":"hello world"}
   ```

1. 要获取该函数的跟踪信息，请运行 [sam traces](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-traces.html)。

   ```
   sam traces
   ```

   该跟踪输出类似于以下示例：

   ```
   New XRay Service Graph
     Start time: 2025-02-03 14:31:48+01:00
     End time: 2025-02-03 14:31:48+01:00
     Reference Id: 0 - (Root) AWS::Lambda - sam-app-HelloWorldFunction-y9Iu1FLJJBGD - Edges: []
      Summary_statistics:
        - total requests: 1
        - ok count(2XX): 1
        - error count(4XX): 0
        - fault count(5XX): 0
        - total response time: 5.587
     Reference Id: 1 - client - sam-app-HelloWorldFunction-y9Iu1FLJJBGD - Edges: [0]
      Summary_statistics:
        - total requests: 0
        - ok count(2XX): 0
        - error count(4XX): 0
        - fault count(5XX): 0
        - total response time: 0
   
   XRay Event [revision 3] at (2025-02-03T14:31:48.500000) with id (1-63dd0cc4-3c869dec72a586875da39777) and duration (5.603s)
    - 5.587s - sam-app-HelloWorldFunction-y9Iu1FLJJBGD [HTTP: 200]
    - 4.053s - sam-app-HelloWorldFunction-y9Iu1FLJJBGD
      - 1.181s - Initialization
      - 4.037s - Invocation
        - 1.981s - ## handleRequest
          - 1.840s - ## getPageContents
      - 0.000s - Overhead
   ```

1. 这是一个可以通过互联网访问的公有 API 端点。我们建议您在测试后删除该端点。

   ```
   sam delete
   ```

## 将 Powertools for AWS Lambda（Java）和 AWS CDK 用于跟踪
<a name="java-tracing-cdk"></a>

请按照以下步骤使用 AWS CDK，通过集成的 [Powertools for AWS Lambda（Java）](https://docs.powertools.aws.dev/lambda-java)模块来下载、构建和部署示例 Hello World Java 应用程序。此应用程序实现了基本的 API 后端，并使用 Powertools 发送日志、指标和跟踪。它由 Amazon API Gateway 端点和 Lambda 函数组成。在向 API Gateway 端点发送 GET 请求时，Lambda 函数会使用嵌入式指标格式向 CloudWatch 调用、发送日志和指标，并向 AWS X-Ray 发送跟踪。该函数将返回一条 hello world 消息。

**先决条件**

要完成本节中的步骤，您必须满足以下条件：
+ Java 11 或更高版本
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [AWS CDK 版本 2](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_prerequisites)
+ [AWS SAM CLI 版本 1.75 或更高版本](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)。如果您使用的是旧版本的 AWS SAM CLI，请参阅[升级 AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/manage-sam-cli-versions.html#manage-sam-cli-versions-upgrade)。

**部署示例 AWS CDK 应用程序**

1. 为您的新应用程序创建一个项目目录。

   ```
   mkdir hello-world
   cd hello-world
   ```

1. 初始化该应用程序。

   ```
   cdk init app --language java
   ```

1. 使用以下命令创建 Maven 项目：

   ```
   mkdir app
   cd app
   mvn archetype:generate -DgroupId=helloworld -DartifactId=Function -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
   ```

1. 打开 `hello-world\app\Function` 目录中的 `pom.xml`，并将现有代码替换为以下代码，其中包括 Powertools 的依赖项和 Maven 插件。

   ```
   <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>helloworld</groupId>
     <artifactId>Function</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>Function</name>
     <url>http://maven.apache.org</url>
   <properties>
       <maven.compiler.source>11</maven.compiler.source>
       <maven.compiler.target>11</maven.compiler.target>
       <log4j.version>2.17.2</log4j.version>
   </properties>
       <dependencies>
           <dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>3.8.1</version>
               <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>software.amazon.lambda</groupId>
               <artifactId>powertools-tracing</artifactId>
               <version>1.3.0</version>
           </dependency>
           <dependency>
               <groupId>software.amazon.lambda</groupId>
               <artifactId>powertools-metrics</artifactId>
               <version>1.3.0</version>
           </dependency>
           <dependency>
               <groupId>software.amazon.lambda</groupId>
               <artifactId>powertools-logging</artifactId>
               <version>1.3.0</version>
           </dependency>
           <dependency>
               <groupId>com.amazonaws</groupId>
               <artifactId>aws-lambda-java-core</artifactId>
               <version>1.2.2</version>
           </dependency>
           <dependency>
               <groupId>com.amazonaws</groupId>
               <artifactId>aws-lambda-java-events</artifactId>
               <version>3.11.1</version>
           </dependency>
     </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.codehaus.mojo</groupId>
               <artifactId>aspectj-maven-plugin</artifactId>
               <version>1.14.0</version>
               <configuration>
                   <source>${maven.compiler.source}</source>
                   <target>${maven.compiler.target}</target>
                   <complianceLevel>${maven.compiler.target}</complianceLevel>
                   <aspectLibraries>
                       <aspectLibrary>
                           <groupId>software.amazon.lambda</groupId>
                           <artifactId>powertools-tracing</artifactId>
                       </aspectLibrary>
                       <aspectLibrary>
                           <groupId>software.amazon.lambda</groupId>
                           <artifactId>powertools-metrics</artifactId>
                       </aspectLibrary>
                       <aspectLibrary>
                           <groupId>software.amazon.lambda</groupId>
                           <artifactId>powertools-logging</artifactId>
                       </aspectLibrary>
                   </aspectLibraries>
               </configuration>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
           <plugin>
                   <groupId>org.apache.maven.plugins</groupId>
                   <artifactId>maven-shade-plugin</artifactId>
                   <version>3.4.1</version>
                   <executions>
                       <execution>
                           <phase>package</phase>
                           <goals>
                               <goal>shade</goal>
                           </goals>
                           <configuration>
                               <transformers>
                                   <transformer
                                           implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer">
                                   </transformer>
                               </transformers>
                               <createDependencyReducedPom>false</createDependencyReducedPom>
                               <finalName>function</finalName>
   
                           </configuration>
                       </execution>
                   </executions>
                   <dependencies>
                       <dependency>
                           <groupId>com.github.edwgiz</groupId>
                           <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
                           <version>2.15</version>
                       </dependency>
                   </dependencies>
           </plugin>
       </plugins>
   </build>
   </project>
   ```

1. 创建 `hello-world\app\src\main\resource` 目录并为日志配置创建 `log4j.xml`。

   ```
   mkdir -p src/main/resource
   cd src/main/resource
   touch log4j.xml
   ```

1. 打开 `log4j.xml` 并添加以下代码。

   ```
   <?xml version="1.0" encoding="UTF-8"?>
   <Configuration>
       <Appenders>
           <Console name="JsonAppender" target="SYSTEM_OUT">
               <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" />
           </Console>
       </Appenders>
       <Loggers>
           <Logger name="JsonLogger" level="INFO" additivity="false">
               <AppenderRef ref="JsonAppender"/>
           </Logger>
           <Root level="info">
               <AppenderRef ref="JsonAppender"/>
           </Root>
       </Loggers>
   </Configuration>
   ```

1. 打开 `hello-world\app\Function\src\main\java\helloworld` 目录中的 `App.java`，并将现有代码替换为以下代码。这是适用于 Lambda 函数的代码。

   ```
   package helloworld;
   
   import java.io.BufferedReader;
   import java.io.IOException;
   import java.io.InputStreamReader;
   import java.net.URL;
   import java.util.HashMap;
   import java.util.Map;
   import java.util.stream.Collectors;
   
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
   import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
   import org.apache.logging.log4j.LogManager;
   import org.apache.logging.log4j.Logger;
   import software.amazon.lambda.powertools.logging.Logging;
   import software.amazon.lambda.powertools.metrics.Metrics;
   import software.amazon.lambda.powertools.tracing.CaptureMode;
   import software.amazon.lambda.powertools.tracing.Tracing;
   
   import static software.amazon.lambda.powertools.tracing.CaptureMode.*;
   
   /**
    * Handler for requests to Lambda function.
    */
   public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
       Logger log = LogManager.getLogger(App.class);
   
   
       @Logging(logEvent = true)
       @Tracing(captureMode = DISABLED)
       @Metrics(captureColdStart = true)
       public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
           Map<String, String> headers = new HashMap<>();
           headers.put("Content-Type", "application/json");
           headers.put("X-Custom-Header", "application/json");
   
           APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
                   .withHeaders(headers);
           try {
               final String pageContents = this.getPageContents("https://checkip.amazonaws.com");
               String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents);
   
               return response
                       .withStatusCode(200)
                       .withBody(output);
           } catch (IOException e) {
               return response
                       .withBody("{}")
                       .withStatusCode(500);
           }
       }
       @Tracing(namespace = "getPageContents")
       private String getPageContents(String address) throws IOException {
           log.info("Retrieving {}", address);
           URL url = new URL(address);
           try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
               return br.lines().collect(Collectors.joining(System.lineSeparator()));
           }
       }
   }
   ```

1. 打开 `hello-world\src\main\java\com\myorg` 目录中的 `HelloWorldStack.java`，并将现有代码替换为以下代码。此代码将使用 [Lambda 构造函数](https://docs.aws.amazon.com/cdk/api/v1/java/aws_cdk.aws_lambda.html)和 [ApiGatewayv2 构造函数](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigatewayv2-readme.html)创建 REST API 和 Lambda 函数。

   ```
   package com.myorg;
   
   import software.amazon.awscdk.*;
   import software.amazon.awscdk.services.apigatewayv2.alpha.*;
   import software.amazon.awscdk.services.apigatewayv2.integrations.alpha.HttpLambdaIntegration;
   import software.amazon.awscdk.services.apigatewayv2.integrations.alpha.HttpLambdaIntegrationProps;
   import software.amazon.awscdk.services.lambda.Code;
   import software.amazon.awscdk.services.lambda.Function;
   import software.amazon.awscdk.services.lambda.FunctionProps;
   import software.amazon.awscdk.services.lambda.Runtime;
   import software.amazon.awscdk.services.lambda.Tracing;
   import software.amazon.awscdk.services.logs.RetentionDays;
   import software.amazon.awscdk.services.s3.assets.AssetOptions;
   import software.constructs.Construct;
   
   import java.util.Arrays;
   import java.util.List;
   
   import static java.util.Collections.singletonList;
   import static software.amazon.awscdk.BundlingOutput.ARCHIVED;
   
   public class HelloWorldStack extends Stack {
       public HelloWorldStack(final Construct scope, final String id) {
           this(scope, id, null);
       }
   
       public HelloWorldStack(final Construct scope, final String id, final StackProps props) {
           super(scope, id, props);
   
           List<String> functionPackagingInstructions = Arrays.asList(
                   "/bin/sh",
                   "-c",
                   "cd Function " +
                           "&& mvn clean install " +
                           "&& cp /asset-input/Function/target/function.jar /asset-output/"
           );
           BundlingOptions.Builder builderOptions = BundlingOptions.builder()
                   .command(functionPackagingInstructions)
                   .image(Runtime.JAVA_11.getBundlingImage())
                   .volumes(singletonList(
                           // Mount local .m2 repo to avoid download all the dependencies again inside the container
                           DockerVolume.builder()
                                   .hostPath(System.getProperty("user.home") + "/.m2/")
                                   .containerPath("/root/.m2/")
                                   .build()
                   ))
                   .user("root")
                   .outputType(ARCHIVED);
   
           Function function = new Function(this, "Function", FunctionProps.builder()
                   .runtime(Runtime.JAVA_11)
                   .code(Code.fromAsset("app", AssetOptions.builder()
                           .bundling(builderOptions
                                   .command(functionPackagingInstructions)
                                   .build())
                           .build()))
                   .handler("helloworld.App::handleRequest")
                   .memorySize(1024)
                   .tracing(Tracing.ACTIVE)
                   .timeout(Duration.seconds(10))
                   .logRetention(RetentionDays.ONE_WEEK)
                   .build());
   
           HttpApi httpApi = new HttpApi(this, "sample-api", HttpApiProps.builder()
                   .apiName("sample-api")
                   .build());
   
           httpApi.addRoutes(AddRoutesOptions.builder()
                   .path("/")
                   .methods(singletonList(HttpMethod.GET))
                   .integration(new HttpLambdaIntegration("function", function, HttpLambdaIntegrationProps.builder()
                           .payloadFormatVersion(PayloadFormatVersion.VERSION_2_0)
                           .build()))
                   .build());
   
           new CfnOutput(this, "HttpApi", CfnOutputProps.builder()
                   .description("Url for Http Api")
                   .value(httpApi.getApiEndpoint())
                   .build());
       }
   }
   ```

1. 打开 `hello-world` 目录中的 `pom.xml`，并将现有代码替换为以下代码。

   ```
   <?xml version="1.0" encoding="UTF-8"?>
   <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
            xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
       <modelVersion>4.0.0</modelVersion>
   
       <groupId>com.myorg</groupId>
       <artifactId>hello-world</artifactId>
       <version>0.1</version>
   
       <properties>
           <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
           <cdk.version>2.70.0</cdk.version>
           <constructs.version>[10.0.0,11.0.0)</constructs.version>
           <junit.version>5.7.1</junit.version>
       </properties>
   
       <build>
           <plugins>
               <plugin>
                   <groupId>org.apache.maven.plugins</groupId>
                   <artifactId>maven-compiler-plugin</artifactId>
                   <version>3.8.1</version>
                   <configuration>
                       <source>1.8</source>
                       <target>1.8</target>
                   </configuration>
               </plugin>
   
               <plugin>
                   <groupId>org.codehaus.mojo</groupId>
                   <artifactId>exec-maven-plugin</artifactId>
                   <version>3.0.0</version>
                   <configuration>
                       <mainClass>com.myorg.HelloWorldApp</mainClass>
                   </configuration>
               </plugin>
           </plugins>
       </build>
   
       <dependencies>
           <!-- AWS Cloud Development Kit -->
           <dependency>
               <groupId>software.amazon.awscdk</groupId>
               <artifactId>aws-cdk-lib</artifactId>
               <version>${cdk.version}</version>
           </dependency>
           <dependency>
               <groupId>software.constructs</groupId>
               <artifactId>constructs</artifactId>
               <version>${constructs.version}</version>
           </dependency>
           <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <version>${junit.version}</version>
             <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>software.amazon.awscdk</groupId>
               <artifactId>apigatewayv2-alpha</artifactId>
               <version>${cdk.version}-alpha.0</version>
           </dependency>
           <dependency>
               <groupId>software.amazon.awscdk</groupId>
               <artifactId>apigatewayv2-integrations-alpha</artifactId>
               <version>${cdk.version}-alpha.0</version>
           </dependency>
       </dependencies>
   </project>
   ```

1. 确保您位于 `hello-world` 目录中然后部署应用程序。

   ```
   cdk deploy
   ```

1. 获取已部署应用程序的 URL：

   ```
   aws cloudformation describe-stacks --stack-name HelloWorldStack --query 'Stacks[0].Outputs[?OutputKey==`HttpApi`].OutputValue' --output text
   ```

1. 调用 API 端点：

   ```
   curl -X GET <URL_FROM_PREVIOUS_STEP>
   ```

   如果成功，您将会看到如下响应：

   ```
   {"message":"hello world"}
   ```

1. 要获取该函数的跟踪信息，请运行 [sam traces](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-traces.html)。

   ```
   sam traces
   ```

   该跟踪输出类似于以下示例：

   ```
   New XRay Service Graph
     Start time: 2025-02-03 14:59:50+00:00
     End time: 2025-02-03 14:59:50+00:00
     Reference Id: 0 - (Root) AWS::Lambda - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: [1]
      Summary_statistics:
        - total requests: 1
        - ok count(2XX): 1
        - error count(4XX): 0
        - fault count(5XX): 0
        - total response time: 0.924
     Reference Id: 1 - AWS::Lambda::Function - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: []
      Summary_statistics:
        - total requests: 1
        - ok count(2XX): 1
        - error count(4XX): 0
        - fault count(5XX): 0
        - total response time: 0.016
     Reference Id: 2 - client - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: [0]
      Summary_statistics:
        - total requests: 0
        - ok count(2XX): 0
        - error count(4XX): 0
        - fault count(5XX): 0
        - total response time: 0
   
   XRay Event [revision 1] at (2025-02-03T14:59:50.204000) with id (1-63dd2166-434a12c22e1307ff2114f299) and duration (0.924s)
    - 0.924s - sam-app-HelloWorldFunction-YBg8yfYtOc9j [HTTP: 200]
    - 0.016s - sam-app-HelloWorldFunction-YBg8yfYtOc9j
      - 0.739s - Initialization
      - 0.016s - Invocation
        - 0.013s - ## lambda_handler
          - 0.000s - ## app.hello
      - 0.000s - Overhead
   ```

1. 这是一个可以通过互联网访问的公有 API 端点。我们建议您在测试后删除该端点。

   ```
   cdk destroy
   ```

## 使用 ADOT 分析您的 Java 函数
<a name="java-adot"></a>

ADOT 提供完全托管式 Lambda [层](chapter-layers.md)，这些层使用 OTel SDK，将收集遥测数据所需的一切内容打包起来。通过使用此层，您可以在不必修改任何函数代码的情况下，对您的 Lambda 函数进行分析。您还可以将您的层配置为对 OTel 进行自定义初始化。有关更多信息，请参阅 ADOT 文档中的[适用于 Lambda 上的 ADOT 收集器的自定义配置](https://aws-otel.github.io/docs/getting-started/lambda#custom-configuration-for-the-adot-collector-on-lambda)。

对于 Java 运行时，您可以在两个层之间选择使用：
+ **适用于 ADOT Java 的 AWS 托管式 Lambda 层（自动分析代理）**- 此层将在启动时自动转换您的函数代码，以收集跟踪数据。有关如何与 ADOT Java 代理配合使用此层的详细说明，请参阅[适用于 OpenTelemetry 的 AWS 发行版对于 Java 的 Lambda 支持（自动分析代理）](https://aws-otel.github.io/docs/getting-started/lambda/lambda-java-auto-instr)。
+ **适用于 ADOT Java 的 AWS 托管式 Lambda 层** - 此层还可为 Lambda 函数提供内置分析，但其需要手动对代码进行一些更改，才能初始化 OTel SDK。有关如何使用此层的详细说明，请参阅 ADOT 文档中的[适用于 OpenTelemetry 的 AWS 发行版对于 Java 的 Lambda 支持](https://aws-otel.github.io/docs/getting-started/lambda/lambda-java)。

## 使用 X-Ray SDK 分析您的 Java 函数
<a name="java-xray-sdk"></a>

要记录关于您的函数对应用程序中的其他资源和服务进行调用的数据，您可以将适用于 Java 的 X-Ray SDK 添加到您的构建配置中。以下示例显示了一种 Gradle 构建配置，其中包括激活 AWS SDK for Java 2.x 客户端自动分析的库。

**Example [build.gradle](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java/build.gradle) – 跟踪依赖项**  

```
dependencies {
    implementation platform('software.amazon.awssdk:bom:2.16.1')
    implementation platform('com.amazonaws:aws-xray-recorder-sdk-bom:2.11.0')
    ...
    implementation 'com.amazonaws:aws-xray-recorder-sdk-core'
    implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk'
    implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor'
    ...
}
```

在添加正确的依赖项并进行必要的代码更改后，请通过 Lambda 控制台或 API 激活函数配置中的跟踪。

## 使用 Lambda 控制台激活跟踪
<a name="java-tracing-console"></a>

要使用控制台切换 Lambda 函数的活动跟踪，请按照以下步骤操作：

**打开活跃跟踪**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 选择 **Configuration**（配置），然后选择 **Monitoring and operations tools**（监控和操作工具）。

1. 在**其他监控工具**下，选择**编辑**。

1. 在 **CloudWatch 应用程序信号和 AWS X-Ray** 下，为 **Lambda 服务跟踪**选择**启用**。

1. 选择**保存**。

## 使用 Lambda API 激活跟踪
<a name="java-tracing-api"></a>

借助 AWS CLI 或 AWS SDK 在 Lambda 函数上配置跟踪，请使用以下 API 操作：
+ [UpdateFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateFunctionConfiguration.html)
+ [GetFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html)
+ [CreateFunction](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html)

以下示例 AWS CLI 命令对名为 **my-function** 的函数启用活跃跟踪。

```
aws lambda update-function-configuration --function-name my-function \
--tracing-config Mode=Active
```

跟踪模式是发布函数版本时版本特定配置的一部分。您无法更改已发布版本上的跟踪模式。

## 使用 CloudFormation 激活跟踪
<a name="java-tracing-cloudformation"></a>

要对 CloudFormation 模板中的 `AWS::Lambda::Function` 资源激活跟踪，请使用 `TracingConfig` 属性。

**Example [function-inline.yml](https://github.com/awsdocs/aws-lambda-developer-guide/blob/master/templates/function-inline.yml) – 跟踪配置**  

```
Resources:
  function:
    Type: [AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html)
    Properties:
      TracingConfig:
        Mode: Active
      ...
```

对于 AWS Serverless Application Model (AWS SAM) `AWS::Serverless::Function` 资源，请使用 `Tracing` 属性。

**Example [template.yml](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs/template.yml) – 跟踪配置**  

```
Resources:
  function:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      Tracing: Active
      ...
```

## 解释 X-Ray 跟踪
<a name="java-tracing-interpretation"></a>

您的函数需要权限才能将跟踪数据上载到 X-Ray。在 Lambda 控制台中激活跟踪后，Lambda 会将所需权限添加到函数的[执行角色](lambda-intro-execution-role.md)。如果没有，请将 [AWSXRayDaemonWriteAccess](https://console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess) 策略添加到执行角色。

在配置活跃跟踪后，您可以通过应用程序观察特定请求。[X-Ray 服务图](https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html#xray-concepts-servicegraph)将显示有关应用程序及其所有组件的信息。以下示例显示了具有两个函数的应用程序。主函数处理事件，有时会返回错误。位于顶部的第二个函数将处理第一个函数的日志组中显示的错误，并使用 AWS SDK 调用 X-Ray、Amazon Simple Storage Service (Amazon S3) 和 Amazon CloudWatch Logs。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sample-errorprocessor-servicemap.png)


X-Ray 无法跟踪对应用程序的所有请求。X-Ray 将应用采样算法确保跟踪有效，同时仍会提供所有请求的一个代表性样本。采样率是每秒 1 个请求和 5% 的其他请求。您无法为函数配置此 X-Ray 采样率。

在 X-Ray 中，*跟踪*记录有关由一个或多个*服务*处理的请求的信息。Lambda 会每个跟踪记录 2 个分段，这些分段将在服务图上创建两个节点。下图突出显示了这两个节点：

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/xray-servicemap-function.png)


位于左侧的第一个节点表示接收调用请求的 Lambda 服务。第二个节点表示特定的 Lambda 函数。以下示例显示了一个包含这 2 个分段的跟踪。两者都命名为 **my-function**，但其中一个函数具有 `AWS::Lambda` 源，另一个则具有 `AWS::Lambda::Function` 源。如果 `AWS::Lambda` 分段显示错误，则表示 Lambda 服务存在问题。如果 `AWS::Lambda::Function` 分段显示错误，则说明函数存在问题。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/V2_sandbox_images/my-function-2-v1.png)


此示例将展开 `AWS::Lambda::Function` 分段，以显示其三个子分段。

**注意**  
AWS 目前正在实施对 Lambda 服务的更改。由于这些更改，您可能会看到 AWS 账户 中不同 Lambda 函数发出的系统日志消息和跟踪分段的结构和内容之间存在细微差异。  
此处显示的示例跟踪说明了旧样式函数分段。以下段落介绍了新旧样式分段之间的差异。  
这些更改将在未来几周内实施，除中国和 GovCloud 区域外，所有 AWS 区域 的函数都将过渡到使用新格式的日志消息和跟踪分段。

旧样式函数分段包含以下子分段：
+ **初始化** – 表示加载函数和运行[初始化代码](foundation-progmodel.md)所花费的时间。此子分段仅对由您的函数的每个实例处理的第一个事件显示。
+ **调用** – 表示执行处理程序代码花费的时间。
+ **开销** – 表示 Lambda 运行时为准备处理下一个事件而花费的时间。

新样式函数分段不包含 `Invocation` 子分段。而是将客户子分段直接附加到函数分段。有关新旧样式函数分段结构的更多信息，请参阅 [了解 X-Ray 跟踪](services-xray.md#services-xray-traces)。

**注意**  
[Lambda SnapStart](snapstart.md) 函数还包括一个 `Restore` 子分段。`Restore` 子分段会显示 Lambda 还原快照、加载运行时和运行任何还原后[运行时挂钩](snapstart-runtime-hooks.md)所花费的时间。恢复快照的过程可能包含在 MicroVM 之外的活动上花费的时间。该时间在 `Restore` 子分段中报告。您无需为在 microVM 之外还原快照所花费的时间付费。

您还可以分析 HTTP 客户端、记录 SQL 查询以及使用注释和元数据创建自定义子段。有关更多信息，请参阅《AWS X-Ray 开发人员指南》**中的 [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html)。

**定价**  
作为 AWS 免费套餐的组成部分，您可以每月免费使用 X-Ray 跟踪，但不能超过一定限制。超出该阈值后，X-Ray 会对跟踪存储和检索进行收费。有关更多信息，请参阅 [AWS X-Ray 定价](https://aws.amazon.com/xray/pricing/)。

## 在层中存储运行时依赖项 (X-Ray SDK)
<a name="java-tracing-layers"></a>

如果您使用 X-Ray 开发工具包来分析AWS开发工具包客户端和您的函数代码，则您的部署程序包可能会变得相当大。为了避免每次更新函数代码时上载运行时依赖项，请将 X-Ray SDK 打包到 [Lambda 层](chapter-layers.md)中。

以下示例显示了存储适用于 Java 的 适用于 Java 的 AWS SDK 和 X-Ray SDK 的 `AWS::Serverless::LayerVersion` 资源。

**Example [template.yml](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-java/template.yml) – 依赖项层**  

```
Resources:
  function:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      CodeUri: build/distributions/blank-java.zip
      Tracing: Active
      Layers:
        - !Ref libs
      ...
  libs:
    Type: [AWS::Serverless::LayerVersion](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-layerversion.html)
    Properties:
      LayerName: blank-java-lib
      Description: Dependencies for the blank-java sample app.
      ContentUri: build/blank-java-lib.zip
      CompatibleRuntimes:
        - java25
```

使用此配置，仅在更改运行时依赖项时您才会更新库层。由于函数部署软件包仅包含您的代码，因此可以帮助缩短上传时间。

为依赖项创建层要求更改构建配置才能在部署之前生成层存档。有关工作示例，请参阅 GitHub 上的 [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) 示例应用程序。

## 示例应用程序中的 X-Ray 跟踪 (X-Ray SDK)
<a name="java-tracing-samples"></a>

本指南的 GitHub 存储库包括演示如何使用 X-Ray 跟踪的示例应用程序。每个示例应用程序都包含用于轻松部署和清理的脚本、一个 AWS SAM 模板和支持资源。

**Java 中的 Lambda 应用程序示例**
+ [example-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/example-java) – 一个 Java 函数，演示如何使用 Lambda 来处理顺序。此函数说明了如何定义和反序列化自定义输入事件对象，如何使用 AWS SDK 和输出日志记录。
+ [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) – 具有单元测试和变量日志记录配置的最小 Java 函数的集合。
+ [java-events](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events) – Java 函数的集合，其中包含用于处理来自 Amazon API Gateway、Amazon SQS 和 Amazon Kinesis 等各种服务的事件的框架代码。这些函数使用最新版本的 [aws-lambda-events](java-package.md) 库（3.0.0 及更新版本）。这些示例不需要 AWS 开发工具包作为依赖项。
+ [s3-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java) – 此 Java 函数可处理来自 Amazon S3 的通知事件，并使用 Java 类库（JCL）从上传的图像文件创建缩略图。
+ [layer-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-java) – 一个 Java 函数，说明如何使用 Lambda 层将依赖项与核心函数代码分开打包。

所有示例应用程序都为 Lambda 功能启用了活动跟踪。例如，`s3-java` 应用程序显示 AWS SDK for Java 2.x 客户端的自动分析、用于测试的段管理、自定义子段以及使用 Lambda 层存储运行时依赖项。

# AWS Lambda 的 Java 示例应用程序
<a name="java-samples"></a>

本指南的 GitHub 存储库提供了演示如何在 AWS Lambda 使用 Java 的示例应用程序。每个示例应用程序都包含用于轻松部署和清理的脚本、一个 CloudFormation 模板和支持资源。

**Java 中的 Lambda 应用程序示例**
+ [example-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/example-java) – 一个 Java 函数，演示如何使用 Lambda 来处理顺序。此函数说明了如何定义和反序列化自定义输入事件对象，如何使用 AWS SDK 和输出日志记录。
+ [java-basic](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-basic) – 具有单元测试和变量日志记录配置的最小 Java 函数的集合。
+ [java-events](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events) – Java 函数的集合，其中包含用于处理来自 Amazon API Gateway、Amazon SQS 和 Amazon Kinesis 等各种服务的事件的框架代码。这些函数使用最新版本的 [aws-lambda-events](java-package.md) 库（3.0.0 及更新版本）。这些示例不需要 AWS 开发工具包作为依赖项。
+ [s3-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/s3-java) – 此 Java 函数可处理来自 Amazon S3 的通知事件，并使用 Java 类库（JCL）从上传的图像文件创建缩略图。
+ [layer-java](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-java) – 一个 Java 函数，说明如何使用 Lambda 层将依赖项与核心函数代码分开打包。

**在 Lambda 上运行常见 Java 框架**
+ [spring-cloud-function-samples](https://github.com/spring-cloud/spring-cloud-function/tree/3.2.x/spring-cloud-function-samples/function-sample-aws)：此示例来自 Spring，展示了如何使用 [Spring Cloud Function](https://spring.io/projects/spring-cloud-function) 框架创建 AWS Lambda 函数。
+ [无服务器 Spring Boot 应用程序演示](https://github.com/aws-samples/serverless-java-frameworks-samples/tree/main/springboot)：该示例展示了如何在带有 SnapStart 和不带有 SnapStart 的托管式 Java 运行时系统中设置典型的 Spring Boot 应用程序，或者如何使用自定义运行时系统设置为 GraalVM 本机映像。
+ [无服务器 Micronaut 应用程序演示](https://github.com/aws-samples/serverless-java-frameworks-samples/tree/main/micronaut)：该示例展示了如何在带有 SnapStart 和不带有 SnapStart 的托管式 Java 运行时系统中使用 Micronaut，或者如何使用自定义运行时系统设置为 GraalVM 本机映像。在[《Micronaut/Lambda 指南》](https://guides.micronaut.io/latest/tag-lambda.html)中了解更多信息。
+ [无服务器 Quarkus 应用程序演示](https://github.com/aws-samples/serverless-java-frameworks-samples/tree/main/quarkus)：该示例展示了如何在带有 SnapStart 和不带有 SnapStart 的托管式 Java 运行时系统中使用 Quarkus，或者如何使用自定义运行时系统设置为 GraalVM 本机映像。在[《Quarkus/Lambda 指南》](https://quarkus.io/guides/aws-lambda)和[《Quarkus/SnapStart 指南》](https://quarkus.io/guides/aws-lambda-snapstart)中了解更多信息。

如果您刚接触用 Java 编写的 Lambda 函数，不妨先尝试 `java-basic` 示例。要开始使用 Lambda 事件源，请参阅 `java-events` 示例。这两种示例都会显示 Lambda 的 Java 库、环境变量、AWS SDK 和 AWS X-Ray SDK 的使用情况。这些示例需要最少的设置，并且可以在不到一分钟的时间内从命令行部署。