Lambda 関数ハンドラーは、イベントを処理する関数コード内のメソッドです。関数が呼び出されると、Lambda はハンドラーメソッドを実行します。関数は、ハンドラーが応答を返すか、終了するか、タイムアウトするまで実行されます。
このページでは、プロジェクトのセットアップオプション、命名規則、ベストプラクティスなど、Java で Lambda 関数ハンドラーを使用する方法について説明します。このページには、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon Simple Storage Service (Amazon S3) バケットに配置する Java Lambda 関数の例も含まれています。関数を書き込んだ後にデプロイする方法については、「.zip または JAR ファイルアーカイブで Java Lambda 関数をデプロイする」または「コンテナイメージを使用した Java Lambda 関数のデプロイ」を参照してください。
セクション
Java ハンドラープロジェクトのセットアップ
Java で Lambda 関数を使用する場合、このプロセスにはコードの記述、コンパイル、コンパイルされたアーティファクトの Lambda へのデプロイが含まれます。Java Lambda プロジェクトはさまざまな方法で初期化できます。例えば、Lambda 関数の Maven Archetype
一般的な Java Lambda 関数プロジェクトは、次の一般的な構造に従います。
/project-root
└ src
└ main
└ java
└ example
└ OrderHandler.java (contains main handler)
└ <other_supporting_classes>
└ build.gradle OR pom.xml
Maven または Gradle を使用してプロジェクトを構築し、依存関係を管理できます。
関数のメインハンドラーロジックは、src/main/java/example
ディレクトリの Java ファイルにあります。このページの例では、このファイルに OrderHandler.java
という名前を付けます。このファイルとは別に、必要に応じて追加の Java クラスを含めることができます。関数を Lambda にデプロイする際は、呼び出し中に Lambda が呼び出すメインハンドラーメソッドを含む Java クラスを必ず指定してください。
Java Lambda 関数のコードの例
以下の Java 21 Lambda 関数コードの例では、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon S3 バケットに配置します。
例 OrderHandler.java
Lambda 関数
package example;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.nio.charset.StandardCharsets;
/**
* Lambda handler for processing orders and storing receipts in S3.
*/
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
private static final S3Client S3_CLIENT = S3Client.builder().build();
/**
* Record to model the input event.
*/
public record Order(String orderId, double amount, String item) {}
@Override
public String handleRequest(Order event, Context context) {
try {
// Access environment variables
String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}
// Create the receipt content and key destination
String receiptContent = String.format("OrderID: %s\nAmount: $%.2f\nItem: %s",
event.orderId(), event.amount(), event.item());
String key = "receipts/" + event.orderId() + ".txt";
// Upload the receipt to S3
uploadReceiptToS3(bucketName, key, receiptContent);
context.getLogger().log("Successfully processed order " + event.orderId() +
" and stored receipt in S3 bucket " + bucketName);
return "Success";
} catch (Exception e) {
context.getLogger().log("Failed to process order: " + e.getMessage());
throw new RuntimeException(e);
}
}
private void uploadReceiptToS3(String bucketName, String key, String receiptContent) {
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// Convert the receipt content to bytes and upload to S3
S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
} catch (S3Exception e) {
throw new RuntimeException("Failed to upload receipt to S3: " + e.awsErrorDetails().errorMessage(), e);
}
}
}
この OrderHandler.java
ファイルには以下のコードのセクションが含まれます:
-
package example
: Java では、これは任意のものにできますが、プロジェクトのディレクトリ構造と一致する必要があります。ここでは、ディレクトリ構造がsrc/main/java/example
であるため、package example
を使用します。 -
import
ステートメント: これらを使用して、Lambda 関数に必要な Java クラスをインポートします。 -
public class OrderHandler ...
: これは Java クラスを定義します。また、有効なクラス定義である必要があります。 -
private static final S3Client S3_CLIENT ...
: これにより、クラスのメソッドの外部で S3 クライアントが初期化されます。これにより、Lambda は初期化フェーズ中にこのコードを実行します。 -
public record Order ...
: このカスタム Java レコードで予想される入力イベントの形状を定義します。 -
public String handleRequest(Order event, Context context)
: これはメインハンドラーメソッドで、メインアプリケーションロジックが含まれています。 -
private void uploadReceiptToS3(...) {}
: これは、メインhandleRequest
ハンドラーメソッドによって参照されるヘルパーメソッドです。
次の build.gradle
または pom.xml
ファイルは、この関数に付随します。
この関数が正しく機能するには、実行ロールで s3:PutObject
アクションを許可する必要があります。また、必ず RECEIPT_BUCKET
環境変数を定義してください。呼び出しに成功したら、Amazon S3 バケットに受信ファイルが含まれているはずです。
注記
この関数がタイムアウトすることなく正常に実行されるには、追加の構成設定が必要になる場合があります。メモリを 256 MB、タイムアウトを 10 秒に設定することをお勧めします。コールドスタートにより、最初の呼び出しにさらに時間がかかる場合があります。実行環境が再利用されるため、それ以降の呼び出しはより高速に実行されます。
Java ハンドラーの有効なクラス定義
クラスを定義するために、aws-lambda-java-core
RequestHandler
インターフェイスは、入力タイプと出力タイプの 2 つのパラメータを受け取る汎用タイプです。どちらのタイプもオブジェクトであることが必要です。この例では、OrderHandler
クラスは RequestHandler<OrderHandler.Order, String>
を実装します。入力タイプはクラス内で定義する Order
レコードで、出力タイプは String
です。
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
...
}
このインターフェイスを使用すると、Java ランタイムはイベントを入力タイプのオブジェクトに逆シリアル化し、出力をテキストにシリアル化します。組み込みのシリアル化が入力タイプと出力タイプで機能する場合は、このインターフェイスを使用します。
独自のシリアル化を使用するために、RequestStreamHandler
インターフェイスを実装できます。このインターフェイスでは、Lambda はハンドラーに入力ストリームと出力ストリームを渡します。ハンドラーは、入力ストリームからバイトを読み取り、出力ストリームに書き込み、void を返します。Java 21 ランタイムを使用したこの例については、「HandlerStream.java
Java 関数で基本型と汎用型 (String
、Integer
、=List
または Map
) のみを使用している場合は、インターフェイスを実装する必要はありません。例えば、関数が Map<String, String>
入力を受け取り、String
を返す場合、クラス定義とハンドラー署名は次のようになります。
public class ExampleHandler {
public String handleRequest(Map<String, String> input, Context context) {
...
}
}
さらに、インターフェイスを実装しない場合、コンテキストオブジェクトはオプションです。例えば、クラス定義とハンドラー署名は次のようになります。
public class NoContextHandler {
public String handleRequest(Map<String, String> input) {
...
}
}
ハンドラーの命名規則
Java の Lambda 関数では、RequestHandler
または RequestStreamHandler
インターフェイスを実装する場合、メインハンドラーメソッドの名前は handleRequest
である必要があります。また、handleRequest
メソッドの上に @Override
タグを含めます。関数を Lambda にデプロイするときは、関数の設定でメインハンドラーを次の形式で指定します。
-
<package>
.<Class>
– 例:example.OrderHandler
。
RequestHandler
または RequestStreamHandler
インターフェイスを実装しない Java の Lambda 関数の場合、ハンドラーには任意の名前を使用できます。関数を Lambda にデプロイするときは、関数の設定でメインハンドラーを次の形式で指定します。
-
<package>
.<Class>
::<handler_method_name>
– 例:example.Handler::mainHandler
。
入力イベントオブジェクトの定義とアクセス
JSON は Lambda 関数の最も一般的な標準入力形式です。この例では、関数は以下のような入力を想定しています:
{
"orderId": "12345",
"amount": 199.99,
"item": "Wireless Headphones"
}
Java 17 以降で Lambda 関数を使用する場合、予想される入力イベントの形状を Java レコードとして定義できます。この例では、Order
オブジェクトを表すレコードを OrderHandler
クラス内で定義します。
public record Order(String orderId, double amount, String item) {}
このレコードは、予想される入力形状と一致します。レコードを定義したら、レコード定義に準拠する JSON 入力を取り込むハンドラー署名を記述できます。Java ランタイムは、この JSON を Java オブジェクトに自動的に逆シリアル化します。その後、オブジェクトのフィールドにアクセスできます。例えば、event.orderId
は元の入力から orderId
の値を取得します。
注記
Java レコードは、Java 17 ランタイム以降の機能です。すべての Java ランタイムで、クラスを使用してイベントデータを表すことができます。このような場合は、jackson
その他の入力イベントタイプ
Java の Lambda 関数には、多くの入力イベントが考えられます。
-
Integer
Long
、Double
、など - イベントは、追加の形式のない数値です (3.5
など)。Java ランタイムは、値を指定されたタイプのオブジェクトに変換します。 -
String
- イベントは、引用符を含む JSON 文字列です (“My string”
など)。ランタイムは、値を引用符なしのString
オブジェクトに変換します。 -
List<Integer>
List<String>
、List<Object>
、など - イベントは JSON 配列です。ランタイムは、それを指定されたタイプまたはインターフェイスのオブジェクトに逆シリアル化します。 -
InputStream
- イベントは任意の JSON タイプです。ランタイムは、ドキュメントのバイトストリームを変更せずにハンドラーに渡します。入力を逆シリアル化し、出力を出力ストリームに書き込みます。 -
ライブラリタイプ - 他の AWS サービスによって送信されるイベントの場合、aws-lambda-java-events
ライブラリのタイプを使用します。例えば、Lambda 関数が Amazon Simple Queue Service (SQS) によって呼び出された場合、 SQSEvent
オブジェクトを入力として使用します。
Lambda コンテキストオブジェクトへのアクセスと使用
Lambda コンテキストオブジェクトには、呼び出し、関数、および実行環境に関する情報が含まれます。この例では、コンテキストオブジェクトは com.amazonaws.services.lambda.runtime.Context
型で、メインハンドラー関数の 2 番目の引数です。
public String handleRequest(Order event, Context context) {
...
}
クラスで RequestHandler
AWS SDK を使用して他のサービスを呼び出す場合、コンテキストオブジェクトはいくつかの重要な領域で必要です。例えば、Amazon CloudWatch の関数ログを生成するには、context.getLogger()
メソッドを使用してログ記録用の LambdaLogger
オブジェクトを取得できます。この例では、何らかの理由で処理が失敗した場合、ロガーを使用してエラーメッセージをログに記録できます。
context.getLogger().log("Failed to process order: " + e.getMessage());
ログ記録以外では、関数のモニタリングにコンテキストオブジェクトを使用することもできます。コンテキストオブジェクトの詳細については、「Lambda コンテキストオブジェクトを使用して Java 関数の情報を取得する」を参照してください。
ハンドラーでの AWS SDK for Java v2 の使用
多くの場合、Lambda 関数を使用して、他の AWS リソースとやり取りしたり、更新したりします。これらのリソースとインターフェイスする最も簡単な方法は、AWS SDK for Java v2 を使用することです。
注記
AWS SDK for Java (v1) はメンテナンスモードであり、2025 年 12 月 31 日にサポートが終了する予定です。今後は AWS SDK for Java v2 のみを使用することをお勧めします。
SDK 依存関係を関数に追加するには、Gradle の場合は build.gradle
に、Maven の場合は pom.xml
ファイルに追加します。関数に必要なライブラリのみを追加することをお勧めします。前のコード例では、software.amazon.awssdk.services.s3
ライブラリを使用しました。Gradle では、build.gradle
の依存関係セクションに次の行を追加することで、この依存関係を追加できます。
implementation 'software.amazon.awssdk:s3:2.28.29'
Maven で、pom.xml
の <dependencies>
セクションに次の行を追加します。
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.28.29</version>
</dependency>
注記
これは SDK の最新バージョンではない場合があります。アプリケーションに適したバージョンの SDK を選択します。
次に、依存関係を Java クラスに直接インポートします。
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
次に、コード例は次のように Amazon S3 クライアントを初期化します。
private static final S3Client S3_CLIENT = S3Client.builder().build();
この例では、関数を呼び出すたびに Amazon S3 クライアントを初期化する必要がないように、メインハンドラー関数の外で Amazon S3 クライアントを初期化しました。SDK クライアントを初期化したら、SDK クライアントを使用して他の AWS サービスとやり取りできます。サンプルコードは、以下のように Amazon S3 PutObject
API を呼び出します。
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// Convert the receipt content to bytes and upload to S3
S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
環境変数にアクセスする
ハンドラーコードでは、System.getenv()
メソッドを使用して任意の環境変数を参照できます。この例では、以下のコード行を使用して、定義された RECEIPT_BUCKET
環境変数を参照します:
String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}
グローバルな状態を使用する
Lambda は、関数を初めて呼び出す前の初期化フェーズで静的コードとクラスコンストラクターを実行します。初期化中に作成されたリソースは呼び出し間でメモリに保持されるため、関数を呼び出すたびにリソースを作成する必要がなくなります。
コード例では、S3 クライアント初期化コードはメインハンドラーメソッドの外にあります。ランタイムは、関数が最初のイベントを処理する前にクライアントを初期化し、クライアントはすべての呼び出しで再利用できます。
Java Lambda 関数のコードのベストプラクティス
Lambda 関数をビルドするときは、次のリストのガイドラインに従って、コーディングのベストプラクティスを実行してください。
-
Lambda ハンドラーをコアロジックから分離します。これにより、関数の単体テストが実行しやすくなります。
-
関数のデプロイパッケージ内で依存関係を制御します。AWS Lambda 実行環境には多数のライブラリが含まれています。最新の機能やセキュリティ更新プログラムを有効にするために、Lambda はこれらのライブラリを定期的に更新します。この更新により、Lambda 関数の動作が微妙に変化する場合があります。関数で使用する依存関係を完全に制御するには、すべての依存関係をデプロイパッケージでパッケージングします。
-
依存関係の複雑さを最小限に抑えます。フレームワークを単純化して、実行環境起動時のロードを高速化します。たとえば、Spring Framework
などの複雑なフレームワークよりも、Dagger や Guice などの単純な Java 依存関係インジェクション (IoC) フレームワークを使用します。 -
デプロイパッケージをランタイムに必要な最小限のサイズにします。これにより、呼び出しに先立ってデプロイパッケージをダウンロードして解凍する所要時間が短縮されます。Java で記述された関数の場合は、デプロイパッケージの一部として AWS SDK ライブラリ全体をアップロードしないようにしてください。代わりに、SDK のコンポーネントを必要に応じて選別するモジュール (DynamoDB、Amazon S3 SDK モジュール、Lambda コアライブラリ
など) を使用します。
-
実行環境の再利用を活用して関数のパフォーマンスを向上させます。関数ハンドラー外で SDK クライアントとデータベース接続を初期化し、静的なアセットを
/tmp
ディレクトリにローカルにキャッシュします。関数の同じインスタンスで処理された後続の呼び出しは、これらのリソースを再利用できます。これにより、関数の実行時間が短縮され、コストが節約されます。呼び出し間でデータが漏れるのを防ぐため、実行環境を使用してセキュリティ上の懸念があるユーザーデータ、イベント、またはその他の情報を保存しないでください。関数がハンドラー内のメモリに保存できない変更可能な状態に依存している場合は、ユーザーごとに個別の関数または個別のバージョンの関数を作成することを検討してください。
-
keep-alive ディレクティブを使用して永続的な接続を維持します。Lambda は、時間の経過とともにアイドル状態の接続を消去します。関数を呼び出すときにアイドル状態の接続を再利用しようとすると、接続エラーが発生します。永続的な接続を維持するには、ランタイムに関連付けられている keep-alive ディレクティブを使用します。例については、「Node.js で Keep-alive を使用して接続を再利用する」を参照してください。
-
環境変数を使用して、オペレーショナルパラメータを関数に渡します。たとえば、Amazon S3 バケットに書き込む場合、書き込み先のバケット名はハードコーディングせずに、環境変数として設定します。
-
Lambda 関数では、再帰呼び出しを使用しないでください。関数が自身を呼び出すこともあれば、新たに開始されたプロセスで関数が再度呼び出される可能性もあります。これを行うと意図しないボリュームで関数が呼び出され、料金が急増する可能性があります。意図しない呼び出しがいくつも見つかった場合は、すぐに関数の予約済同時実行数を
0
に設定して、コードを更新している間のすべての関数の呼び出しをスロットリングします。 -
Lambda 関数コードで文書化されていない非公開の API を使用しないでください。AWS Lambda マネージドランタイムでは、Lambda が Lambda の内部 API にセキュリティと機能面の更新を定期的に適用します。これらの内部 API 更新には後方互換性がないことがあり、関数にこれらの非公開 API に対する依存関係がある場合、呼び出しの失敗などの意図しない結果につながります。公開されている API のリストについては、「API リファレンス」を参照してください。
-
冪等性コードを記述します。関数の記述に冪等性コードを使用すると、重複するイベントが同じ方法で処理されるようになります。コードでは、イベントを適切に検証し、重複するイベントを適切に処理する必要があります。詳細については、「Lambda 関数を冪等にするにはどうすればよいですか?
」を参照してください。
-
Java DNS キャッシュを使用しない Lambda 関数は、既に DNS レスポンスをキャッシュしています。別の DNS キャッシュを使用すると、接続タイムアウトが発生する可能性があります。
java.util.logging.Logger
クラスは JVM DNS キャッシュを間接的に有効にできます。デフォルト設定を上書きするには、 を初期化する前に networkaddress.cache.ttlを 0 に設定します logger
。例:public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }
-
Java で記述されたデプロイパッケージを Lambda で解凍する所要時間を短縮します。そのために、依存する
.jar
ファイルを別個の /lib ディレクトリにファイルします。これで関数のすべてのコードを多数の.class
ファイルと一緒に単一の Jar に収納するよりも高速化されます。手順については、「.zip または JAR ファイルアーカイブで Java Lambda 関数をデプロイする」を参照してください。