Spring: @Async needs @EnableAsync and a public method on another bean

Like @Transactional it is proxy-based, so the same self-invocation trap applies.

Code
@EnableAsync
@Configuration
class AsyncConfig {
    @Bean
    Executor taskExecutor() {
        var ex = new ThreadPoolTaskExecutor();
        ex.setCorePoolSize(4);
        ex.setQueueCapacity(100);
        ex.setThreadNamePrefix("mail-");
        ex.initialize();
        return ex;
    }
}

@Async
public CompletableFuture<Void> sendEmail(String to) { ... }
Output
Caller returns immediately.
Thread name: mail-1  (not http-nio-8080-exec-1)
Advertisement

Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.

Published 2026-08-11