In this post, we will be covering Spring Events which is a way to loosely couple component in your application for better design and easy exchange of information.
Spring Events are part of the Spring Framework for the very easy phase of the 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 events, with the introduction of the 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 that is handled transparently in a similar fashion as @Autowired
and others: no extra configuration is necessary with java config
To understand it more clearly, 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)
In this example, we will create simple CustomerRegistrationEvent
which will be used 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, we were required to extend ApplicationEvent
which is no longer required, this change has its own merits as it is much easier to publish events while it can be hard to trek events in large applications.
As part of our example, we need a listener which should listen to the event being published and make sure to act accordingly in case event published by the service is of interest (if given listener is registered for the given event)
@Component
public class CustomerRegistrationEventListener {
@EventListener
void handleRegistration(CustomerRegistrationEvent event){
System.out.println("Registration event got triggered for customer:: " + event.getName());
}
}
With Spring 4.2, you are not required to implement ApplicationListener
or something similar but a simple @EventListener
annotation is sufficient 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 – all related event handlers can be grouped into one class.
We will create a publisher which will be responsible for the following tasks
We need ApplicationEventPublisher
in our code to simply publish given event.
@Autowired
private ApplicationEventPublisher publisher;
public void publishEvent(final String name) {
publisher.publishEvent(new CustomerRegistrationEvent(name));
}
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
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());
}
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, this will not be invoked at all.
In this post, we got an introduction to Spring Events.We learned how we can create custom events and how to use publish Spring events synchronously and asynchronously.We briefly covered as to how to use condition to filter events and transaction capabilities of the Spring Framework event system.
Hello!! I am Umesh- an engineer by profession and a photographer by passion.I like to build stuff on the web using OSS and love to capture the world through my lens.