Mastering Spring Event Mechanism: Custom Events, Listeners, Async & Ordering
This article introduces Spring's event mechanism, covering the creation of custom ApplicationEvent classes, implementing listeners via ApplicationListener and @EventListener annotations, publishing events, using @Async for asynchronous handling, configuring custom event multicaster, and controlling listener execution order with @Order, complete with code examples.
Environment: Spring 5.3.23
1. Introduction
Spring's event mechanism is a tool for handling internal or external events within the framework. It follows the observer design pattern and consists of three main parts: events, publishers, and listeners.
In Spring, an event is the core object; a publisher emits events, and a listener processes them. Event objects encapsulate the source and related information, enabling communication between the source and listeners. Publishers are typically injected via ApplicationEventPublisher, while listeners implement the ApplicationListener interface.
The Spring container itself publishes many events and also supports custom events. In Spring Boot, you can publish events using ApplicationEventPublisher and define custom events by extending ApplicationEvent. Listeners are defined by implementing ApplicationListener.
Overall, Spring's event mechanism is a useful tool for managing and handling system events.
2. Event Publishing and Listening
Creating and publishing a custom event by extending ApplicationEvent :
<code>static class OrderEvent extends ApplicationEvent {
private Order order;
public OrderEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return this.order;
}
}</code>Implementing a listener for the custom event:
<code>static class OrderListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent event) {
System.out.printf("Listener received event: %s", event.getOrder().toString());
}
}</code>Publishing the custom event via ApplicationEventPublisher :
<code>static class OrderService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher eventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void save(Order order) {
this.eventPublisher.publishEvent(new OrderEvent("create order event", new Order()));
}
}</code>Annotation‑based listener using @EventListener :
<code>static class MyEventListener {
@EventListener
public void receiveOrderEvent(OrderEvent event) {
System.out.printf("Listener received event: %s", event.getOrder().toString());
}
}</code>Listening to multiple events:
<code>@EventListener({OrderEvent.class, UserEvent.class})
public void receiveOrderEvent() {
System.out.println("Listener received defined event");
}</code>Using ApplicationEvent as a generic parameter:
<code>@EventListener({OrderEvent.class, UserEvent.class})
public void receiveOrderEvent(ApplicationEvent event) {
System.out.printf("Listener received event: %s", event.getSource());
}</code>Filtering events with SpEL expressions:
<code>// User object
static class User {
private Integer type;
public User(Integer type) { this.type = type; }
}
// UserEvent extending ApplicationEvent
static class UserEvent extends ApplicationEvent {
private User user;
public UserEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() { return user; }
}
// Listener that only triggers when user.type == 1
@EventListener(condition = "#event.user.type == 1")
public void receiveOrderEvent(UserEvent event) {
System.out.printf("Listener received event: %s", event.getUser());
}</code>3. Asynchronous Event Listening
Method 1: Use @Async on an @EventListener method:
<code>@EventListener
@Async
public void receiveOrderEvent(UserEvent event) {
System.out.printf("%s - Listener received event: %s", Thread.currentThread().getName(), event.getUser());
}</code>Result example:
<code>SimpleAsyncTaskExecutor-1 - Listener received event: com.pack.main.events.EventMain$User@abb3dc</code>Method 2: Define a custom event multicaster with a dedicated executor:
<code>@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(Executors.newSingleThreadExecutor());
return multicaster;
}</code>Spring will automatically register this multicaster during container initialization. Example execution result:
<code>pool-1-thread-1 - Listener received event: com.pack.main.events.EventMain$User@50f8a6</code>Important notes for asynchronous listeners:
If an async listener throws an exception, it will not propagate to the caller; handle it via AsyncUncaughtExceptionHandler .
Async listener methods cannot return values to trigger subsequent events; manually inject ApplicationEventPublisher if you need to publish further events.
4. Event Listener Order
Use the @Order annotation to define execution precedence; lower values have higher priority:
<code>@EventListener
@Order(1)
public void receiveOrderEvent1(UserEvent event) {
System.out.printf("%s - Listener received event - 1: %s%n", Thread.currentThread().getName(), event.getUser());
}
@EventListener
@Order(0)
public void receiveOrderEvent2(UserEvent event) {
System.out.printf("%s - Listener received event - 2: %s%n", Thread.currentThread().getName(), event.getUser());
}</code>Execution result demonstrates that the method with @Order(0) runs before the one with @Order(1) :
<code>main - Listener received event - 2: com.pack.main.events.EventMain$User@96bacf
main - Listener received event - 1: com.pack.main.events.EventMain$User@96bacf</code>Done!!!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.