【Java】Springで非同期処理

実行環境

Springの非同期化方法

  1. Configurationクラスに@EnableAsyncを付与する
  2. Executorを定義する
  3. 非同期化したいメソッドに@Asyncを付与する
  4. @Asyncを付与したメソッドを呼ぶ

@Asyncを使わない方法は以下を参照

stmtk358.hatenablog.com

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)については以下を参照

stmtk358.hatenablog.com

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については以下を参照

stmtk358.hatenablog.com

エラーハンドリング

  • 戻り値がある場合(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) {
        // エラーハンドリング
    }

呼び出し元でエラーハンドリングできるが、メインスレッドがブロッキングされる

参考文献

qiita.com

spring.pleiades.io

docs.spring.io

www.baeldung.com