Synchronous and Asynchronous Spring Events in One Application

10 April 2015 Jakub Novotný

Events in Spring framework are very powerful, cleanly designed and easy to use tool. You can find a lot of tutorials describing basics of events and listener configuration, but if you want configure several event listeners, some of them blocking and some of them asynchronous, here comes the problem. The main purpose of this article is to show how to design and register your own event multicaster which can do the stuff, but let's start easy...

Source Code

Clone or download the source code of the final version of the application with tagged versions from GitHub.

Basic Application

I want to demonstrate event configuration step by step on the very simple Spring MVC based web application. I will try to keep the application as simple as possible.

Now we have just MVC application with one controller which can greet us on the /hello/{name} url. Every spring configuration is done in single Spring MVC application context file WEB-INF/dispatcher-servlet.xml. There is just component scanning for controllers and view resolver for jsp files for now.

Synchronous Application Events

Let's assume that we want to monitor greetings. It's just a stupid example, but it is enough for explanation. At first, we have to create event class. Events in Spring are based on class hierarchy. Every event must extends the ApplicationEvent class. Let's create the UserGreeted class for event and it's payload GreetingData class which is just DTO around the string.

OK, now we want to fire the event somehow. Spring has the predefined bean implementing the ApplicationEventPublisher interface for this purpose. We just need to inject it into our controller. We can do it by implementing the ApplicationEventPublisherAware interface with the proper setter method, or we can just autowire it. Let's do it in the simpler way by the @Autowire annotation. Then you can just create the new event and publish it via the publisher. So our controller will now look like this:

Now we can define the listener that can handle this event. To achieve this, we will create the package eu.keyup.listener, define the component scanning by typing

to the dispetcher-servlet.xml and we will add the GreetingListener to this package.

The listener for the UserGreeted event must implement ApplicationListener with generic type UserGreeted (or something above the UserGreeted in the class hierarchy). Without any other configuration, the Spring will handle the event synchronously. That means that if you publish the event, it searches for all the listeners and calls their onApplicationEvent methods. The rest of the code in your method is executed after all those methods. This also means you can e.g. throw the exception in the listener and it will propagate to your method, which can be useful for validation, or you can modify some data inside the event. This is our case, our rude listener appends some string to the original name. If you now deploy the app and opens the url /hello/John, it will print Hello John (sorry, but John sounds really stupid...).

Asynchronous Listeners

OK, synchronous events have some advantages, but what if you want to execute some long running task inside the listener? You definitely don't want your controller action to wait until that task will finish. You can tell Spring to handle the events asynchronously. To achieve this, you must redefine the ApplicationEventMulticaster bean with id applicationEventMulticaster. The purpose of this bean is to handle all events and deliver them to the registered listeners. It exists in the Spring application context by default and we can force the asynchronous behavior by providing the thread pool executor to it. You can simply add the following lines to our application context configuration file dispatcher-servlet.xml.

It is crucial that the bean's id equals to applicationEventMulticaster. The asynchronous configuration is achieved by setting the taskExecutor property. Every event is now handled inside this pool.

Distributive Event Multicaster

So now we will get just "Hello John" message in the browser and the opinion of the listener is written to the log file only. But what if we want to register some other listener which should process the event synchronously? Can we do it? Of course! Spring is awesome! But we must do a little programming stuff around that (thank God, it was just configuration, configuration and configuration until now...). We need to provide our own implementation of the ApplicationEventMulticaster. We will surely not write all the logic again. We just create DistributiveEventMulticaster bean with two other multicasters injected. One synchronous and one asynchronous.

Before we start to implement the multicaster, we need to differentiate between synchronous and asynchronous events. We will define annotation for this purpose.

This is just the void annotation without any attributes which we can access via reflection because it's marked as runtime. We suppose that every listener annotated as @AsyncListener should be asynchronous and every other listener should be synchronous. Let's implement the multicaster now.

To be honest, there is not as much programming as I promised. Our class just implements ApplicationEventMulticaster and calls methods of synchronous or asynchronous multicaster. The main logic is in the addApplicationListener method where we just pick the correct multicaster. The last step is to register the multicaster with correct properties as an application event multicaster...

...and we are done. Sync now, every listener annotated with @AsyncListener will be asynchronous and every other synchronous. You can download the source code of the final version of the application with tagged versions from GitHub. It contains git repository with tagged previous versions. Have fun!