AWS Lambda で Dagger 2 を使う
AWS Lambda + API Gateway で Serverless Application を構築する際に Dagger 2 を使うサンプルです。
AWS Lambda 関数を使用する際のベストプラクティス - AWS Lambda において、Java では Dagger の利用が推奨されています。
依存関係の複雑さを最小限に抑えます。フレームワークを単純化して、実行コンテキスト起動時のロードの高速化を優先します。たとえば、Spring Framework などの複雑なフレームワークよりも、Dagger や Guice などの単純な Java 依存関係インジェクション (IoC) フレームワークを使用します。
アプリケーション構成
- ApiHandler: Lambda のエントリポイント
- Controller: ServerlessInput / ServerlessOutput を操作する
- Service: 実際の処理を行う
ライブラリ
- Dagger 2.14
Dagger 系クラスの説明
- アプリケーション全体でインスタンスを共有するための
AppComponent
,AppModule
, (@Singletone
annotation) - HTTPリクエスト単位でインスタンスを生成・共有するための
RequestComponent
,RequestModule
,@Request
annotation RequestComponent
はAppComponent
の Subcomponent とし、リクエストを処理するタイミングで生成する (appComponent.newRequest()
)RequestComponent
の生成時には、そのリクエストに紐づいた情報をRequestModule
を使って各オブジェクトに渡すようにする (RequestModule
のコンストラクタに設定する)- (TODO Binding Instances が使えるかもしれない。ただし型ではなく名前ベースになる)
Dagger 関連ソースコード
di/AppComponent.java
@Singleton @Component(modules = {AppModule.class}) public interface AppComponent { RequestComponent newRequest(RequestModule requestModule); }
di/AppModule.java
@Module public class AppModule { @Provides @Singleton public static AuthModel provideAuthModel() { return new AuthModel(); } }
di/RequestScope.java
@Scope @Retention(RetentionPolicy.RUNTIME) @interface RequestScope {}
di/RequestComponent.java
@RequestScope @Subcomponent(modules = {RequestModule.class}) public interface RequestComponent { SystemController newSystemController(); UserController newUserController(); }
di/RequestModule.java
@Module public class RequestModule { private final String serverlessInput; private final Object context; public RequestModule(String serverlessInput, Object context) { this.serverlessInput = serverlessInput; this.context = context; } @Provides public SystemService provideSystemService() { return new SystemService(); } @Provides @RequestScope public UserInfo provideUserInfo() { return new UserInfo(); } @Provides @Named("ServerlessInput") public String provideServerlessInput() { return serverlessInput; } @Provides @Named("Context") public Object provideContext() { return context; } }
アプリケーションソースコード
build.gradle
plugins { id 'java-library' id "net.ltgt.apt" version "0.10" id 'eclipse' } repositories { jcenter() } dependencies { api 'org.apache.commons:commons-math3:3.6.1' testCompile 'org.jmockit:jmockit:1.42' testCompile 'org.junit.jupiter:junit-jupiter-api:5.2.0' implementation 'com.google.guava:guava:23.0' implementation 'org.apache.httpcomponents:httpclient:4.5.6' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0' testRuntime "org.junit.platform:junit-platform-launcher:1.2.0" compile 'com.google.dagger:dagger:2.17' apt 'com.google.dagger:dagger-compiler:2.17' }
app/ApiHandler.java
public class ApiHandler { private final AppComponent appComponent; public static void main(String[] args) { ApiHandler handler = new ApiHandler(); String result = handler.handleRequest("version", "context"); System.out.println(result); String result2 = handler.handleRequest("version", "context"); System.out.println(result2); String result3 = handler.handleRequest("user/aaa", "context"); System.out.println(result3); } public ApiHandler() { System.out.println("Create handler"); appComponent = DaggerAppComponent.create(); } public String handleRequest(String input, String context) { System.out.println("---start ApiHandler#handleRequest()---"); RequestModule requestModule = new RequestModule(input, context); RequestComponent requestComponent = appComponent.newRequest(requestModule); if (input.startsWith("user")) { UserController uc = requestComponent.newUserController(); return uc.getUser();
app/UserController.java
public class UserController { private final UserService userService; private final UserInfo userInfo; private final String serverlessInput; private final Object context; @Inject public UserController( UserService userService, UserInfo userInfo, @Named("ServerlessInput") String serverlessInput, @Named("Context") Object context) { System.out.println("Create SystemController"); this.userService = userService; this.userInfo = userInfo; this.serverlessInput = serverlessInput; this.context = context; }