【Java】Springで非同期処理
実行環境
Springの非同期化方法
- Configurationクラスに@EnableAsyncを付与する
- Executorを定義する
- 非同期化したいメソッドに@Asyncを付与する
- @Asyncを付与したメソッドを呼ぶ
@Asyncを使わない方法は以下を参照
1. Configurationクラスに@EnableAsyncを付与する
@EnableAsync @Configuration public class AsyncConfiguration {...}
SpringBootの場合はApplicationクラスでもOK
@EnableAsync @SpringBootApplication public class AsyncApplication {...}
2. Executorを定義する
Configuration(Application)クラスにExecutorのBeanを定義をする
@Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setThreadNamePrefix("TaskThread-"); executor.initialize(); return executor; }
Executor(TaskExecutor)については以下を参照
3. 非同期化したいメソッドに@Asyncを付与する
@Service public class AsyncService { @Async("taskExecutor") public void execute() { // do something } }
4. @Asyncを付与したメソッドを呼ぶ
@RestController @AllArgsConstructor public class AsyncController { private final AsyncService asyncService; @GetMapping("/") public String index() { asyncService.execute(); return ""; } }
@Asyncの制約
- 非同期化できるのはpublicメソッドのみ
- らしいのだが、publicじゃなくても(パッケージプライベートでも)実行できた
- 同一クラス内のメソッドは非同期化できない
- 戻り値はFuture(CompletableFuture)型でラップする必要がある
- Future(CompletableFuture)型にしなくても実行はできるが、戻り値は受け取れない
CompletableFutureについては以下を参照
エラーハンドリング
- 戻り値がある場合(Future型)
- Future.get()がチェック例外をスローする
- 戻り値がない場合(void型)
- 例外は呼び出し元に伝播されない
- AsyncUncaughtExceptionHandlerでハンドリングする
- CompletableFuture
を返すようし、Future.get()でハンドリングする
- 例外は呼び出し元に伝播されない
Future.get()でハンドリング
CompletableFuture<String> cf = service.execute(); try { String result = cf.get(); } catch (InterruptedException | ExecutionException e) { // エラーハンドリング }
AsyncUncaughtExceptionHandlerでハンドリング
@Configuration public class AsyncConfiguration implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { // エラーハンドリング }; } }
AsyncUncaughtExceptionHandlerの場合、呼び出し元でエラーハンドリングができない
CompletableFuture<Void>でハンドリング
CompletableFuture<Void> cf = service.execute(); try { cf.get(); } catch (InterruptedException | ExecutionException e) { // エラーハンドリング }
呼び出し元でエラーハンドリングできるが、メインスレッドがブロッキングされる