

# 使用 .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 层将依赖项与核心函数代码分开打包。