Annotation-driven event listeners in Spring 4.2+

Posted: 2015-09-29 in Tricks & Tips
Tags: , , , ,

Learn how to reduce boilerplace code in event handling with annotation-driven event listeners in Spring 4.2+.


Exchanging events within the application has become indispensable part of many applications and thankfully Spring provides a complete infrastructure for transient events (*). The recent refactoring of transaction bound events gave me an excuse to check in practice the new annotation-driven event listeners introduced in Spring 4.2. Let’s see what can be gained.

(*) – for persistent events in Spring-based application Duramen could be a solution that is worth to see

Spring logo

The old way

To get a notification about an event (both Spring event and custom domain event) a component implementing ApplicationListener with onApplicationEvent has to be created.

class OldWayBlogModifiedEventListener implements
                        ApplicationListener<OldWayBlogModifiedEvent> {


    public void onApplicationEvent(OldWayBlogModifiedEvent event) {

It works fine, but for every event a new class has to be created which generates boilerplate code.

In addition our event has to extend ApplicationEvent class – the base class for all application events in Spring.

class OldWayBlogModifiedEvent extends ApplicationEvent {

    public OldWayBlogModifiedEvent(Blog blog) {

    public Blog getBlog() {
        return (Blog)getSource();

Please notice that using domain objects in the events has notable drawback and is not the best idea in many situations. Pseudodomain objects in the code examples were used to not introduce unnecessary complexity.

Btw, ExternalNotificationSender in this example is an instance of a class which sends external notifications to registered users (e.g. via email, SMS or Slack).

Annotation-driven event listener

Starting with Spring 4.2 to be notified about the new event it is enough to annotate a method in any Spring component with @EventListener annotation.

    public void blogModified(BlogModifiedEvent blogModifiedEvent) {

Under the hood 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.

Conditional event handling

To make @EventListener even more interesting there is an ability to handle only those events of a given type which fulfill given condition(s) written in SpEL. Let’s assume the following event class:

public class BlogModifiedEvent {

    private final Blog blog;
    private final boolean importantChange;

    public BlogModifiedEvent(Blog blog) {
        this(blog, false);

    public BlogModifiedEvent(Blog blog, boolean importantChange) { = blog;
        this.importantChange = importantChange;

    public Blog getBlog() {
        return blog;

    public boolean isImportantChange() {
        return importantChange;

Please note that in the real application there would be probably a hierarchy of Blog related events.
Please also note that in Groovy that class would be much simpler.

To generate event only for important changes the condition parameter can be used:

    @EventListener(condition = "#blogModifiedEvent.importantChange")
    public void blogModifiedSpEL(BlogModifiedEvent blogModifiedEvent) {

Relaxed event type hierarchy

Historically ApplicationEventPublisher had only an ability to publish objects which inherited after ApplicationEvent. Starting with Spring 4.2 the interface has been extended to support any object type. In that case the object is wrapped in PayloadApplicationEvent and sent through.

//base class with Blog field - no need to extend `ApplicationEvent`
class BaseBlogEvent {}

class BlogModifiedEvent extends BaseBlogEvent {}
//somewhere in the code
ApplicationEventPublisher publisher = (...);    //injected

publisher.publishEvent(new BlogModifiedEvent(blog)); //just plain instance of the event

That change makes publishing events even easier. However, on the other hand without an internal conscientiousness (e.g. with marker interface for all our domain events) it can make event tracking even harder, especially in larger applications.

Publishing events in response to

Another nice thing with @EventListener is the fact that in a situation of non-void return type Spring will automatically publish returned event.

    public BlogModifiedResponseEvent blogModifiedWithResponse(BlogModifiedEvent blogModifiedEvent) {
        return new BlogModifiedResponseEvent(
            blogModifiedEvent.getBlog(), BlogModifiedResponseEvent.Status.OK);

Asynchronous event processing

Updated. As rightly suggested by Radek Grębski it is also worth to mention that @EventListener can be easily combined with @Async annotation to provide asynchronous event processing. The code in the particular event listener doesn’t block neither the main code execution nor processing by other listeners.

    @Async    //Remember to enable asynchronous method execution 
              //in your application with @EnableAsync
    public void blogAddedAsync(BlogAddedEvent blogAddedEvent) {

To make it work it is only required to enable asynchronous method execution in general in your Spring context/application with @EnableAsync.


Annotation-driven event listeners introduced in Spring 4.2 continue a trend to reduce boilerplate code in Spring (Boot) based applications. The new approach looks interesting especially for small applications with a small amount of events where a maintenance overhead is lower. In the world of ubiquitous Spring (Boot) magic it is more worthy to remember that with great power comes great responsibility.

In the next blog post I will write how the new mechanism can be also used to simplify handling of transaction bound events.

Please note that Spring Framework 4.2 is a default dependency of Spring Boot 1.3 (at the time of writing 1.3.0.M5 is available). Alternatively it is possible to manually upgrade Spring Framework version in Gradle/Maven for Spring Boot 1.2.5 – it should work for most of the cases.

  1. Very good article, thx!. Whats worth mentioning is that you can execute listener asynchronously by using @Async + @EventListener on a method.

  2. […] >> Annotation-driven event listeners in Spring 4.2+ [solidsoft] […]

  3. Thomas Traude says:

    Great article!

    Would you please explain what you mean by the following statement?
    “[…] using domain objects in the events has notable drawback and is not the best idea in many situations.”
    What are the drawbacks?

    • Thanks Thomas. And by the way a very good question. A separate blog post could be written about that, but in short I would name 3 main drawbacks.
      1. Your core domain is exposed to other part of the system (other bounded context) or in some cases even worse to other systems via a message broker. That makes your domain harder to refactor (every listener has to be adjusted to your changes) and in addition you can’t specify what information would be exposed (can’t limit that) – everything from domain object or nothing. It is usually better to put just those values which are important for given event type.
      2. In the moment of publishing a notification a domain object can be not completed. The common case is persistence with JPA – IDs are generate on flush (usually on commit) while notifications are often sent just after a persist/merge operation was requested (not performed) – then IDs have null value.
      3. Quite often a graph of domain objects (an aggregate) becomes large. The aggregate root can have attached collections with objects having their own dependent collections. In the situation when many different events are published it can become an (unnecessary) bottleneck.

      I hope that those points sound sensible for you. Of course using a separate DTO objects for events isn’t perfect as well.

  4. […] you probably already know (e.g. from my previous blog post) it is no longer needed to create a separate class implementing ApplicationListener with […]

  5. […] you probably already know (e.g. from my previous blog post) it is no longer needed to create a separate class implementing ApplicationListener with […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s