Spring Application Events

Understanding Spring Application Events, built-in events, and custom events.

Serghei Motpan
8 min readFeb 16, 2022

Why use Spring Events?

There are situations when it’s required to bind some actions on a specific level on application startup, to be able to know when some actions are completed we can subscribe to the built-in events raised by spring application context and do the required actions.

On the other hand, in any spring application may rise the need for a specific domain to generate some specific events that should be handled by other domains/modules without any concern about how they will be handled. Spring framework provides a built-in mechanism that comes out of the box with Spring Application Events.

Why should I Use Events Instead of Direct Method Calls?

Direct method calls and events fit different situations. Usually, a method call is used when a method should be executed in the same transaction, or a set of operations that should be atomic, or the code should be executed in the same processing thread. On the other hand, events fit the case when we want the processing to be done in another thread, or in another module/domain (for example: on some task completion we want to send an email).

Using the spring event-based communication model, the sender publishes an event without knowing who the receiver might be. There may be more than one receiver component, also, the receiver doesn’t need to know who is the publisher is. A receiver can listen to multiple events at the same time. In this way, the sender and receiver components are loosely coupled, which makes testing the application easier.

In this post the following topics will be described:

  1. Spring Core built-in Application Events
  2. SpringBoot built-in Application Events
  3. Custom Spring Application Events and how to use them

Spring Core built-in Application Events

Core Events Overview

Spring core framework provides application-level event firing on loading the beans. The ApplicationContext publishes certain types of events on different types of loaded beans.

The pattern behind Spring Application Events firing and event listening is Observer Design Pattern.

To have a better idea of how they work let’s see built-in application events and when they are raised:

ContextRefreshedEvent — is published when the ApplicationContext is either initialized or refreshed. This can also be raised using the refresh() method on the ConfigurableApplicationContext interface.

ContextStartedEvent — is published when the ApplicationContext is started using the start() method on the ConfigurableApplicationContext interface. At this stage you can poll your database or you can restart any stopped application after receiving this event.

ContextStoppedEvent — is published when the ApplicationContext is stopped using the stop() method on the ConfigurableApplicationContext interface. You can do required housekeep work after receiving this event.

ContextClosedEvent — is published when the ApplicationContext is closed using the close() method on the ConfigurableApplicationContext interface. A closed context reaches its end of life; it cannot be refreshed or restarted.

In the following image see the order of event raising:

As you may notice the ContextRefreshedEvent is raised twice at different stages of application startup when the context is initialized and on context refresh.

Under the hood, each implementation of ApplicationContext extends AbstractApplicationContext which implements common context functionality and also encapsulates the core application events raising.

These methods are invoked by the different implementations of ApplicationContext, and you can see at what stage a specific event is raised.

How to listen to the core built-in events?

Before Spring 4.2 to be able to listen to an event you should implement the ApplicationListener<E extends ApplicationEvent> and register to Application Config as @Component or defining as @Bean in your configuration, Java config, or XML config.

Starting with Spring 4.2 now possible to simply annotate a method of a managed bean with @EventListener to automatically register an ApplicationListener, and it doesn’t require additional configs or bean definition.

You can listen for an event by just telling to @EventListener(classes = ContextStartedEvent.class) the classes which you want to trigger the method invocation or telling the method the object that should be passed

In the following image see the execution logs of the events raised order:

Execution logs

SpringBoot built-in Application Events

Springboot built-in Application Events Overview

Let’s see spring-boot built-in application events and when they are raised:

ApplicationStartingEventis sent at the start of a run but before any processing, except for the registration of listeners and initializers.

ApplicationEnvironmentPreparedEventis sent when the Environment to be used in the context is known but before the context is created.

ApplicationContextInitializedEventis sent when the ApplicationContext is prepared and ApplicationContextInitializers have been called but before any bean definitions are loaded.

ApplicationPreparedEventis sent just before the refresh is started but after bean definitions have been loaded.

ApplicationStartedEventis sent after the context has been refreshed but before any application and command-line runners have been called.

AvailabilityChangeEventis sent right after with LivenessState.CORRECT to indicate that the application is considered as live and after with ReadinessState.ACCEPTING_TRAFFIC to indicate that the application is ready to service requests.

ApplicationReadyEventis sent after any application and command-line runners have been called.

ApplicationFailedEventis sent if there is an exception on startup.

The above list only includes SpringApplicationEvents that are tied to a SpringApplication. In addition to these, the following events are also published after ApplicationPreparedEvent and before ApplicationStartedEvent:

A WebServerInitializedEvent is sent after the WebServer is ready. ServletWebServerInitializedEvent and ReactiveWebServerInitializedEvent are the servlet and reactive variants respectively.

A ContextRefreshedEvent is sent when an ApplicationContext is refreshed.

In the following image see the order of event raising:

Some events are triggered before the ApplicationContext is created, so we cannot register a listener on those as a @Bean. We can register listeners for these events by adding the listener manually:

Listen to the Springboot built-in events

If you want those listeners to be registered automatically, regardless of the way the application is created, you can add a META-INF/spring.factories file to your project and reference your listener(s) by using the org.springframework.context.ApplicationListener key, as shown in the following example:

org.springframework.context.ApplicationListener=com.semotpan.springboot.application.events.ApplicationStartingEventHandler

Once we make sure that our event listener is registered properly, we can listen to all of Spring Boot’s SpringApplicationEvents. Let’s have a look at the execution logs during application startup.

Execution logs

You may notice the WebServerInitializedEvent is raised, because as dependency is added

which initializes the web context.

Custom Spring Application Events and how to use them

Application Events Creation

To create a custom event, before Spring 4.2, you’ll have to extend ApplicationEvent

The source which is being passed to super() should be the object on which the event occurred initially or, an associated object with the event.

Starting with Spring 4.2 a custom event doesn’t require anymore to extend the ApplicationEvent interface.

Spring will automatically wrap it in a PayloadApplicationEvent.

Usually, the naming convention for an event is Domain name on which event occurred, the action that triggered the event, which is in the past tense, and the event as a suffix. Examples: UserCreatedEvent, PostCreatedEvent, PostUpdatedEvent.

Publishing Application Events

To send an application event we can use ApplicationEventPublisher, which is provided by Spring Application Context

if the event extends ApplicationEvent, publishing looks like:

Listening to Application Events

There are two ways to define a listener. We can either use the @EventListener annotation or implement the ApplicationListener interface. In either case, the listener class has to be managed by Spring

No additional configuration is required with annotation-driven configuration enabled.

We can bind a listener to as many events as we want

The following example shows how to define an Application Listener by extending the ApplicationListener interface

Conditional Application Listeners

Spring allows listeners to be triggered only in certain circumstances if we specify a SpEL condition

In the example above, the listener will be triggered with PostCreatedEvent only when the #event.status.name() is equal to ‘UPDATE’.

The event will only be handled if the expression evaluates to one of the following values: “true”, “1”, “on”, “yes”.

Synchronous Application Listeners

Listening for an event with @EventListener is done synchronously, this means that the processing is done in the same thread. If the method that raised the event is executed in a transaction, and the event handling fails then the whole transaction will fail.

Also, it’s possible to raise a new event from an event listener by returning a new event from the event listener.

Asynchronous Application Listeners

As mentioned in previous paragraphs, the events by default are handled in a synchronous way. This means that the publisher thread blocks until all the listeners have finished processing the event. When blocking of the thread isn’t wanted we need to enable async handling of our events. To achieve that, we have to annotate the event handler with @Async and expose an Executor into the spring context.

Configuration for enabling the async mode, and, also, a custom Executor to allocate separate threads for handlers

Example of an asynchronous listener

Transaction-bound Application Listeners

Starting with Spring 4.2, the spring provides a new @TransactionalEventListener annotation, which is an extension of @EventListener, that allows binding the listener of an event to a transaction phase.

Available transaction phases:

BEFORE_COMMIT — The listener will handle the event before the transaction is committed.

AFTER_COMMIT — This is the default phase of @TransactionalEventListener. The listener will handle the event when the transaction gets committed successfully.

AFTER_COMPLETION — The listener will handle the event when the transaction is committed or it is rolled back.

AFTER_ROLLBACK — The event will be handled after the transaction had been rolled back.

The following example defines a listener that listens to TransactionalPostCreatedEvent. The event is raised when the starter transaction is being committed.

The listener is bound to the AFTER_COMMIT phase, and it requires a new transaction in order to persist a new PostLog because the previous transaction is about to be committed, thus making it impossible to be reused.

This listener will be invoked only if there’s a transaction in which the event producer is running and it’s about to be committed. If no transaction is running the event isn’t published at all unless we override this by setting fallbackExecution attribute to true.

The following example binds an event to AFTER_ROLLBACK phase

Git Repository

You can access the full project at GitHub here:

https://github.com/semotpan/spring-application-events

Thanks for reading!

--

--