Implementing Spring Event‑Driven Architecture with Asynchronous Listeners
This article explains how to use Spring's event‑driven mechanism to decouple business operations such as saving a product and sending notifications, showing step‑by‑step code for custom events, publishing, asynchronous listeners, and custom thread‑pool configuration to improve resilience and maintainability.
In typical business scenarios, a service method first saves a product to the database and then sends a notification.
@Service
public class ProductServiceImpl {
...
public void saveProduct(Product product) {
productMapper.saveOrder(product);
notifyService.notify(product);
}
...
}When requirements change, additional operations such as persisting to Elasticsearch are added, leading to repeated modifications and coupling issues.
@Service
public class ProductServiceImpl {
...
public void saveProduct(Product product) {
productMapper.saveProduct(product);
esService.saveProduct(product);
notifyService.notify(product);
}
...
}To decouple these steps, Spring’s event‑driven mechanism can be used, consisting of three parts: ApplicationEvent (the event object), ApplicationEventPublisher (the sender), and ApplicationListener (the receiver).
The implementation follows three steps: define a custom event class, publish the event, and listen for it (optionally with @Async for asynchronous handling).
public class ProductEvent extends ApplicationEvent {
public ProductEvent(Product product) {
super(product);
}
} @Service
public class ProductServiceImpl implements IproductService {
@Autowired
private ApplicationEventPublisher publisher;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveProduct(Product product) {
productMapper.saveProduct(product);
// event publishing
publisher.publishEvent(product);
}
...
} @Slf4j
@AllArgsConstructor
public class ProductListener {
private final NotifyService notifyServcie;
@Async
@Order
@EventListener(ProductEvent.class)
public void notify(ProductEvent event) {
Product product = (Product) event.getSource();
notifyServcie.notify(product, "product");
}
}Enable asynchronous processing by adding @EnableAsync to the Spring Boot main class.
@Slf4j
@EnableSwagger2
@SpringBootApplication
@EnableAsync
public class ApplicationBootstrap {
...
}For production use, configure a custom thread pool to replace the default SimpleAsyncTaskExecutor.
@Configuration
public class ExecutorConfig {
private int corePoolSize = 10;
private int maxPoolSize = 50;
private int queueCapacity = 10;
private int keepAliveSeconds = 150;
@Bean("customExecutor")
public Executor myExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("customExecutor-");
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}With this setup, the product‑saving logic stays simple while notifications and other side‑effects are processed independently, improving system resilience and maintainability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
