Spring Events
In this post, we will cover Spring Events which is a way to loosely couple component in your application for better design and easy exchange of information.Spring Framework provides a way for different components to communicate more flexibility with each other than the traditional method calls.In this article, we will try to take a closer look at this design technique and see why it is so powerful and flexible.
Spring framework events are part of the Spring Framework, however, I believe that this is one of the most overlooked features of the Spring Framework. Spring events provide a lot of features and capabilities and one of the most interesting among those are event publishing provided by ApplicationContext
. Application events are not used that frequently in the real world application, however, Spring Framework internally use a lot of these events to communicate various states, with Spring Boot, it has become more interesting.
Spring 4.1 introduced @EventListener
, annotation driven event Listener which removed all the complexities of defining your EventListner.@EventListener
is a core annotation handled transparently in a similar fashion as <a aria-label="@Autowired (opens in a new tab)" href="https://javadevjournal.com/spring-boot/spring-boot-security-auto-configuration/" target="_blank" rel="noreferrer noopener" class="rank-math-link">@Autowired</a>
and others: no extra configuration is necessary with java config.
1. Spring Events vs Method Call
When communicating with other modules, we have the following options while working on a Spring application.
- Using traditional method call.
- Using Spring framework events system
Method call is a traditional or most common way while communicating. In most cases we may need method call where we need response from the other components before we can proceed to the next step(e.g Verifying credit card information during the checkout. We can not place order with invalid credit card or card with no money).
Events on the other hand in a way to notify all parties about a specific steps happens in the process so that all interested parties can take actions. Spring events are good candidates when we want to pass the processing to another thread without blocking the current one. Here are some of the example where events are a good candidates
- Sending out emails to the customer on order placement.
- Sending out notification when order is shipped.
Spring framework provides options to create different spring events bases on our requirement. It also provides the flexibility to create custom events if required.
2. Custom Spring Events
To understand it more, we will create and publish a custom Spring event. Spring provides flexibility to create and publish custom events which by default will be synchronous. We will work on a simple example of customer registration, we will publish a simple CustomerRegistration
event whenever a new customer registers with our application. We will let all listeners handle this event and perform applicable action (e.g. sending email on customer registrations)
2.1 Custom Events
In this example, we will create a simple CustomerRegistrationEvent
to store customer data (in our case just customer name)
public class CustomerRegistrationEvent {
private String name;
public CustomerRegistrationEvent(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Before Spring 4.2, it required us to extend ApplicationEvent
which is no longer required. This change has its own merits as it is much easier to publish events while it’s hard to trek events in large applications.
2.2 Custom Events Listener
As part of our example, we need a listener which should listen to the event being published and act accordingly in case event published by the service is of interest.
@Component
public class CustomerRegistrationEventListener {
@EventListener
void handleRegistration(CustomerRegistrationEvent event){
System.out.println("Registration event got triggered for customer:: " + event.getName());
}
}
With Spring 4.2, it does not require you to implement ApplicationListener
or something similar but a simple @EventListener
annotation suffices to declare it as a Listener.
Spring will create an ApplicationListener
instance for the event with a type taken from the method argument. There is no limitation on the number of annotated methods in one class–we can group all related event handlers into one class.
2.3 Publishing Events
We will create a publisher with following tasks:
- It will create an event object.
- It will publish that event to all who are listening to this event.
We need ApplicationEventPublisher
in our code to publish given event.
@Autowired
private ApplicationEventPublisher publisher;
public void publishEvent(final String name) {
publisher.publishEvent(new CustomerRegistrationEvent(name));
}
3. Async Events
As mentioned earlier, these events work in Async
mode, which means publisher thread will block until all the listeners (who are listing to this event) have finished processing this event.This can lead to a certain issue in terms of application scaling where we want to run these events in Async mode and let rest of the application to work normally.To allow Spring to handle events asynchronously, we need to redefine ApplicationEventMulticaster
with an executor.
@Bean(name = "applicationEventMulticaster")
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
eventMulticaster.setErrorHandler(TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER);
return eventMulticaster;
}
This will enable asynchronously mode at the global level (at the ApplicationContext level), this means that all method annotated with @EventListener
will be executed asynchronously.
Another most easier way is to use @Async annotation
@Async
@EventListener
void handleRegistration(CustomerRegistrationEvent event){
System.out.println("Registration event got triggered for customer:: " + event.getName());
}
We need to make to enable asynchronous method execution by enabling @EnableAsync
4. Filtering
We can also use condition attribute on our listener to filter certain events. Let’s say we want to listen to only CustomerRegistration
event if this is a B2B customer.
@EventListener(condition = "#event.customerType=B2C")
void handleRegistration(CustomerRegistrationEvent event){
System.out.println("Registration event got triggered for customer:: " + event.getName());
}
5. Transaction bound events
Spring provides a way to bound events to a certain phase of a transaction (e.g. publish an event when a transaction is complete). We can use @TransactionalEventListener
annotation to achieve this behavior. When @TransactionalEventListener
is present, Spring will automatically register this in place of default one.
To put it in simple words, this Listener will only invoke if there is a transaction running, in case of no running transaction, it will not invoke this at all.
Summary
In this post, we got an introduction to Spring framework events. We learned how we can create custom events and how to use publish Spring events synchronously and asynchronously. We briefly covered how to use a condition to filter events and transaction capabilities of the Spring Framework event system. As usual, you can download the source code from our GitHub repository
You need to correct the below line (replace ‘Async’ with ‘Sync’)..
“As mentioned earlier, these events work in Async mode, which means publisher thread will block until all the listeners (who are listing to this event) have finished processing this event.”
very simple and helpful tutorial
Happy that it was helpful for you!!
“…Async mode, which means publisher thread will block until all the listeners … have finished processing this event.”
That’s definition of “Sync”, not “Async”
Agree Nikola!! but this line highlight the issue with the Sync mode and why we need the Async mode.This line is to set the context as why we have the need to look for the Async mode.