By default, Spring application litseners operate synchronously within the publisher's thread. Blocking operations in one listener delay subsequent events. To decouple event processing from the main execution flow, Spring offers concurrency support via the @Async annotation.
Enabling asynchronous processing requires a dedicated configuraton class annotated with @EnableAsync. Defining a custom ThreadPoolTaskExecutor ensures controlled resource management rather than relying on the default simple async executor.
@Configuration
@EnableAsync
public class AsyncEventConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("async-event-worker-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
Once the executor is configured, apply the @Async annotation directly to the listener method. This instructs the container to submit the event handling logic to the configured thread pool instead of executing it inline.
@Component
public class RegistrationNotificationHandler implements ApplicationListener<AccountCreatedEvent> {
private static final Logger logger = LoggerFactory.getLogger(RegistrationNotificationHandler.class);
@Override
@Async
public void onApplicationEvent(AccountCreatedEvent event) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.error("Processing interrupted", e);
}
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
logger.info("{} - Notification sent successfully for user: {}", timestamp, event.getUsername());
}
}