AWS Lambda × Java の最短ルート:shadowJar 作成から API Gateway 連携までわかりやすく解説

目次

はじめに

AWS Lambda と API Gateway を使って Java アプリをサーバーレス化しようとすると、 「ClassNotFoundException が出る…」 「API Gateway が 403/500 を返してくる…」 「shadowJar?bootJar?どっちを使えばいいの?」
こんな壁にぶつかった経験はありませんか。
実は、Java を Lambda で動かすには “正しい構成” を知らないと必ずつまずきます。 特に shadowJar・Lambda プロキシ統合・IAM ロールの 3 点は、 理解していないと永遠にエラーが消えません。
本記事では、以下のような方を対象に、 Java(Spring Boot ではない純粋な Java)を AWS Lambda × API Gateway で確実に動かす方法 をわかりやすく解説します。

■対象読者
・Java を AWS Lambda で動かしたいエンジニア
・API Gateway との統合で 403/500 に悩んでいる人
・shadowJar と bootJar の違いがよくわからない人
・Lambda のログが出ずに原因調査が進まない人

■この記事の目的
・shadowJar が必須である理由を理解する
・Lambda プロキシ統合を ON にすべき理由を理解する
・IAM ロールを設定しないとログが出ない理由を理解する
・Java Lambda を “確実に動かすための最短ルート” を提供する

hello-lambdaアプリケーションの作成(spring ベース)

hello-lambdaプロジェクト作成

https://start.spring.io/ よりspring-initializrを開き、hello-lambdaプロジェクトを作成。
<オプション↓>
———————————————————
・project:Gradle-Groovy
・spring boot:4.0.5 (後で変更します)
・Artifact:hello-lambda
・java:21
———————————————————

zipでダウンロードされたhello-lambdaを解凍し、InterlliJ IDEAで開く。
※InterlliJ IDEAが使い慣れていない場合は、自身の使いなれたエディターで問題ありません。

IntelliJ IDEAでのspringプロジェクト作成方法を以下の記事に載せているのでこちらもご参考ください。

あわせて読みたい
【初心者向け】IntelliJでJava+Spring BootのWebアプリを作成する方法 はじめに 本記事では、IntelliJ IDEAを利用してJava+Spring BootのWebアプリのサンプルを作成する手順をご紹介します。spring tool suiteでも可能ですが、私自身の興味...

LambdaHandlerクラスの作成

LambdaHandlerクラスを以下の通り作成する。

LambdaHandler.java

package com.example.hello_lambda.handler;

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 com.example.hello_lambda.HelloLambdaApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;

import java.util.Map;

public class LambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    private static final ApplicationContext context =
            SpringApplication.run(HelloLambdaApplication.class);

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent request, Context contextLambda) {

        String name = request.getQueryStringParameters().getOrDefault("name", "World");

        String body = "Hello, " + name + "!";

        return new APIGatewayProxyResponseEvent()
                .withStatusCode(200)
                .withBody(body);
    }
}

HelloLambdaApplicationの修正

HelloLambdaApplicationを以下の通り修正する。

HelloLambdaApplication.java

package com.example.hello_lambda;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloLambdaApplication {
}

build.gradleの修正

以下の通り修正する。

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.4'
    id 'io.spring.dependency-management' version '1.1.4'
    id 'com.github.johnrengelman.shadow' version '8.1.1'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.3'
    implementation 'com.amazonaws:aws-lambda-java-events:3.11.0'
}
shadowJar {
    archiveBaseName.set("app")
    archiveClassifier.set("")
    archiveVersion.set("")
}

gradle-wrapper.propertiesの修正

gradle/wrapper/gradle-wrapper.propertiesを以下の通り修正する。
※ここではgradleのバージョンを変更しています。

gradle-wrapper.properties

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

ビルド

以下のコマンドを実行し、hello-lambdaをビルドする。

.\gradlew clean shadowJar

ビルドが成功すると、以下のようにbuild/libs/app.jarが作成される。(後続のlambdaで利用。)

lambdaの作成と設定

lambda関数の作成

testという名称でlambda関数を作成。ランタイムにはjava21を選択する。

lambda関数へhello-lamdaのjarファイルをアップロード

以下の手順で前の手順で作成したapp.jarをアップロードする。

lambda設定

「コード」タブのランタイム設定のハンドラを確認すると「example.Hello::handleRequest」のようになっているので、以下の通り修正する。

com.example.hello_lambda.handler.LambdaHandler

テストタブの下部「イベントJSON」に以下のデータを記載する。

イベントJSON

{
  "queryStringParameters": {
    "name": "test"
  }
}

その後「テスト」を実行し、結果が下記画像の通りとなり問題なければ次の手順のIAM Role作成に進む

IAM Role作成

以下の手順で「CloudLogs」というIAMロールを作成する。
(本手順では、API Gatewayを実行した際のログをCloudWatchで確認するために必要な手順となる)

API Gateway作成

API GatewayのIAMロール設定

下記の通り、先ほど作成したIAMロールのARNを用いてAPI Gatewayに設定する。

REST API作成

test-apiという名称でRESTAPIを作成

リソースの作成

下記の通り「hello」という名称でリソースを作成する。

メソッドの作成

helloのリソースに対し、メソッドを作成する。
———————————————————————–
メソッドタイプ:GET
Lambdaプロキシ統合:ON
Lambda関数:自身で作成したLambda関数を選択
———————————————————————–

作成後、APIをステージなしでデプロイする。

ステージを作成

test-stageという名称でステージを作成する。
※一度ステージなしでデプロイしなければステージを作成できないため、前述の手順でデプロイを行っている

デプロイ

先ほど作成したstage(test-stage)でAPIを再デプロイする。

アクセス確認

API Gatewayのステージ画面から「URLを呼び出す」の部分に記載されているURLを確認し、ブラウザでアクセスする。※/hello?name=testが必要であることに留意

「Hello, test!」と表示される。

以上で手順は完了です。後述では、本手順を実施するにあたりつまづいたポイントを紹介します。

つまづきポイント

Point1:Jarをアップロードする際、shadowjarである必要がある

AWS Lambda の Java ランタイムは、通常の Java アプリケーションと同じ ClassLoader でクラスを読み込みます。 そのため、Lambda が読み込める JAR は以下のような “標準的な fat-jar 構造” である必要があります。

  • 依存クラスが JAR の直下に展開されている
  • ClassLoader がそのままクラスを解決できる

Gradle Shadow Plugin が生成する shadowJar はまさにこの形式です。
Spring Boot の bootJar は、Spring Boot Loader を前提とした 特殊な JAR 構造になっています。

BOOT-INF/classes/   ← アプリのクラス
BOOT-INF/lib/       ← 依存ライブラリ
org/springframework/boot/loader/ ← Spring Boot Loader

AWS Lambda の Java ランタイムは Spring Boot Loader を起動しません。 そのため、BOOT-INF 配下のクラスを読み込めず、Lambda 実行時に次のようなエラーが発生します。

java.lang.ClassNotFoundException: com.example.Handler

Point2:Lambda プロキシ統合を設定する必要がある

REST API と Lambda を統合する際は、「Lambda プロキシ統合」 を必ず ON にします。
Lambda プロキシ統合を有効にすると、API Gateway は次のように動作します。

  • HTTP リクエストをそのまま Lambda に渡す
  • Lambda の戻り値をそのまま HTTP レスポンスとして返す

つまり、API Gateway が余計な変換を行わず、 Lambda が API の入口と出口を完全にコントロールできる ようになります。

Point3:IAMロールを作成する必要がある

今回の内容を実施するにあたり、ログを何度も確認しました。そのログを出すために必要なのがIAMロールです。
最初かってにログは出力されるものだと思っていましたが、IAMロールをAPI Gatewayに設定しなければCloudWatchから確認できません。
本記事通りに実施してみても、もしかすると環境制約によってエラーがでたり、コピペミスによってエラーがでたりすることもあるかと思います。そんなときのために必ずIAMロールを作成して、API Gatewayに設定してください。

おわりに

Java を AWS Lambda で動かすのは、Node.js や Python と比べると少しハードルが高いです。 しかし、shadowJar・プロキシ統合・IAM ロールという “つまずきポイント” を正しく理解すれば、 Lambda × API Gateway × Java は驚くほどシンプルに動きます。
この記事が、あなたのエラー解消や理解の助けになっていれば嬉しいです。
もし今回の内容が役に立ったと感じたら、 Java × AWS、サーバーレス、Spring Boot、API 設計 など、 関連する記事もぜひ読んでみてください。
あなたの開発がもっとスムーズに、もっと楽しくなるような情報を、 これからも丁寧に発信していきます。
最後まで読んでいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次