“This is the second day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.

[Note]Spring Events | Baeldung

1. An overview of the

In this tutorial, we’ll discuss how to use events in Spring.

Events are one of the most overlooked features of the framework, but also one of the more useful. Like many other things in Spring, event publishing is one of the capabilities provided by ApplicationContext.

There are some simple guidelines to follow:

  • If we are using versions of the Spring Framework prior to 4.2, the event class should extend ApplicationEvent.Start with version 4.2The event class no longer needs to be extendedApplicationEventClass.
  • Publishers should inject oneApplicationEventPublisherObject.
  • Listeners should be implementedApplicationListenerInterface.

2. Customize events

Spring allows us to create and publish custom events that are synchronized by default. This has some advantages, such as the listener being able to participate in the publisher’s transaction context.

2.1. A simple application event

Let’s create a simple event class — just a placeholder for storing event data.

In this case, the event class contains a String message:

public class CustomSpringEvent extends ApplicationEvent {
    private String message;

    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage(a) {
        returnmessage; }}Copy the code

2.2. The publisher

Now let’s create a publisher for this event. The publisher constructs the event object and publishes it to anyone who is listening.

To publish events, publishers can simply inject ApplicationEventPublisher and use publishEvent () API:

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publishCustomEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message); applicationEventPublisher.publishEvent(customSpringEvent); }}Copy the code

Or, the publisher class can implement ApplicationEventPublisherAware interface, it will also injected event publishers at application startup. In general, it’s easier to inject @Autowire into the publisher.

From the Spring Framework 4.2, ApplicationEventPublisher interface for publishEvent (Object event) method provides a new overloading, this method accepts any Object as events. Therefore, Spring events no longer need to extend the ApplicationEvent class.

2.3. The listener

Finally, let’s create a listener.

The only requirement for a listener is to bea bean and implement the ApplicationListener interface:

@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - "+ event.getMessage()); }}Copy the code

Notice how our custom listener is parameterized with a generic type of custom event, which makes the onApplicationEvent() method type-safe. This also avoids having to check whether an object is an instance of a particular event class and cast it.

Also, as already discussed (Spring events are synchronous by default), the doStuffAndPublishAnEvent() method blocks until all listeners have finished processing the event.

3. Create asynchronous events

In some cases, publishing events synchronously is not really what we want — we may need to process our events asynchronously.

We can create with an executable program ApplicationEventMulticaster bean in the configuration to open it.

SimpleAsyncTaskExecutor does this nicely for us:

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster(a) {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        returneventMulticaster; }}Copy the code

The event, publisher, and listener implementations are the same as before, but now the listener handles the event asynchronously in a separate thread.

4. Existing framework events

Spring itself publishes various out-of-the-box events. For example, ApplicationContext will trigger various framework events: ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent, and so on.

These events give application developers an option to connect to the application’s lifecycle and context and add their own custom logic where needed.

Here’s a quick example of a listener that listens for context refreshes:

public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. "); }}Copy the code

To learn more about existing framework events, check out our next tutorial here.

Annotation-driven event listeners

As of Spring 4.2, an EventListener does not need to bea bean implementing the ApplicationListener interface — it can be registered on any public method of the managed bean via the @eventlistener annotation:

@Component
public class AnnotationDrivenEventListener {
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event."); }}Copy the code

As before, the method signature declares the event type it uses.

By default, listeners are invoked synchronously. However, we can easily make it asynchronous by adding @async annotations. We just need to remember to enable asynchronous support in our applications.

6. Generic support

Events can also be scheduled using generic information in event types.

6.1. Generic application events

Let’s create a generic event type.

In our example, the event class contains anything and a SUCCESS status indicator:

public class GenericSpringEvent<T> {
    private T what;
    protected boolean success;

    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}
Copy the code

Notice the difference between GenericSpringEvent and CustomSpringEvent. We now have the flexibility to publish arbitrary events and no longer need to extend from ApplicationEvent.

6.2. The listener

Now let’s create a listener for this event.

We can define listeners as before by implementing the ApplicationListener interface:

@Component
public class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) {
        System.out.println("Received spring generic event - "+ event.getWhat()); }}Copy the code

Unfortunately, this definition requires us to inherit GenericSpringEvent from the ApplicationEvent class. So, for this tutorial, let’s use the annotation-driven event listener we discussed earlier.

You can also make event listeners conditional by defining Boolean SpEL expressions on the @EventListener annotation.

In this case, the event handler is called only if the String of GenericSpringEvent is successfully called:

@Component
public class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) {
        System.out.println("Received spring generic event - " + event.getWhat());
    }
Copy the code

The Spring Expression Language (SpEL) is a powerful expression language that is covered in detail in another tutorial.

6.3. The publisher

Event publishers are similar. But because of type erasure, we need to publish an event that resolves the generic parameters we will filter, for example, the class GenericStringSpringEvent extends GenericSpringEvent.

In addition, there is an alternative way to publish events. If we return a non-null value as a result from a method using the @EventListener annotation, the Spring Framework sends that result to us as a new event. In addition, we can publish multiple new events by returning them to a collection as a result of event processing.

7. Transaction binding events

This section is about using the @ TransactionalEventListener annotations. To learn more about transaction management, see Using Spring and JPA Transactions.

Since Spring 4.2 framework provides a new @ TransactionalEventListener annotations, it is an extension of the @ EventListener, it allows the event listener is bound to the affairs of a certain stage.

You can bind to the following transaction phases:

  • AFTER_COMMIT(Default) – used in transactionsOn successful completionTrigger event.
  • AFTER_ROLLBACK– If the transactionHas been rolled back
  • AFTER_COMPLETION– If the transactionHas been completed(AFTER_COMMITAFTER_ROLLBACKThe alias)
  • BEFORE_COMMIT– Used in transactionsBefore submittingTrigger event.

Here’s a quick example of a transactional event listener:

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}
Copy the code

This listener is called only if there is a transaction that the event producer is running and is about to commit.

If no transaction is running, no event will be sent at all, unless we override it by setting the fallbackExecution property to true.

Conclusion 8.

In this short article, we covered the basics of handling events in Spring, including creating a simple custom event, publishing it, and then processing it in a listener.

We also looked briefly at how to enable asynchronous processing of events in the configuration.

Then we learned about the improvements introduced in Spring 4.2, such as annotation-driven listeners, better generics support, and event binding to the transaction phase.

As always, the code provided in this article is available on GitHub. This is a Maven-based project, so it should be easy to import and run.