Spring Boot Async Event Listener

Spring boot async event listener uses @Async annotation that enables events listeners to execute in async mode. The annotations @async and @enableasync allow the spring boot event listener to run in an asynchronous mode. Spring boot event listeners are synchronous by default. The application event publisher thread will be blocked until all listeners have completed processing the event. In this post, we’ll look at how to execute a spring boot event listener in an asynchronous manner. In this section, we’ll look at the differences between running listeners synchronously and asynchronously.

All you need to do is apply the @Async annotation on an event listener to have it execute in async mode. We must additionally annotate one of our @Configuration classes or the @SpringBootApplication class with @EnableAsync in order for the @Async annotation to activate.

The @async annotation allows you to create and run code in a multi-threaded environment. The events will be processed in concurrently. The publisher thread will not be blocked. As a result, the publisher thread will continue to execute without waiting for the event listener to finish.



Default Synchronous Event Listener

By default, the spring boot events will be executed synchronously. The publisher thread will be blocked until all listeners have finished processing the events. The synchronous event logs for the following example will illustrate how the publisher thread is blocked and how the listener events are executed sequentially.

MyCustomApplicationListener.java

package com.yawintutor;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

//@EnableAsync
@Configuration
public class MyCustomApplicationListener {

	//@Async
	@EventListener
	public void listener1(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener1      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());
		Thread.sleep(5000);
		System.out.println("Listener1      : completed");
	}

	//@Async
	@EventListener
	public void listener2(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener2      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());	
		Thread.sleep(5000);
		System.out.println("Listener2      : completed");
	}

	//@Async
	@EventListener
	public void listener3(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener3      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());	
		Thread.sleep(5000);
		System.out.println("Listener3      : completed");
	}
}

The spring boot console log will show the beginning of the publishing event, followed by the execution of the listeners one by one, and ultimately the completion of the publisher event. The publisher event thread will be blocked until all of the listener events have finished.

TestController : publishing the message
MyCustomApplicationEventPublisher : Source : com.yawintutor.TestController, Message : MyEvent
Listener1      : Source : com.yawintutor.TestController, Message : MyEvent
Listener1      : completed
Listener2      : Source : com.yawintutor.TestController, Message : MyEvent
Listener2      : completed
Listener3      : Source : com.yawintutor.TestController, Message : MyEvent
Listener3      : completed
TestController : published the message


Asynchronous Event Listener

The annotation @EnableAsync, which is applied to one of the @Configuration classes or the spring boot main method, allows the spring boot application to run in asynchronous mode. To enable the event listener method to execute in asynchronous mode, the @Async annotation should be applied to the listener methods. The following example demonstrates how to configure the event listener to run in asynchronous mode.

MyCustomApplicationListener.java

package com.yawintutor;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@Configuration
public class MyCustomApplicationListener {

	@Async
	@EventListener
	public void listener1(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener1      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());
		Thread.sleep(5000);
		System.out.println("Listener1      : completed");
	}

	@Async
	@EventListener
	public void listener2(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener2      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());	
		Thread.sleep(5000);
		System.out.println("Listener2      : completed");
	}

	@Async
	@EventListener
	public void listener3(MyCustomApplicationEvent event) throws Exception {
		System.out.println("Listener3      : Source : "+event.getSource().getClass().getName() + ", Message : "+ event.getName());	
		Thread.sleep(5000);
		System.out.println("Listener3      : completed");
	}
}

The following log demonstrates that the publisher publishes the event first and then completes the execution without waiting for the listeners to compete the events. Listeners are run in a multi-threaded context. The listeners are executed concurrently and finished simultaneously.

TestController : publishing the message
MyCustomApplicationEventPublisher : Source : com.yawintutor.TestController, Message : MyEvent
TestController : published the message
Listener1      : Source : com.yawintutor.TestController, Message : MyEvent
Listener3      : Source : com.yawintutor.TestController, Message : MyEvent
Listener2      : Source : com.yawintutor.TestController, Message : MyEvent
Listener3      : completed
Listener2      : completed
Listener1      : completed


Application Event Class

The custom application event class is created by extending the ApplicationEvent class. The ApplicationEvent class is able to transfer data from the publisher to the listener classes. The application event class is demonstrated in the following example.

MyCustomApplicationEvent.java

package com.yawintutor;

import org.springframework.context.ApplicationEvent;

@SuppressWarnings("serial")
public class MyCustomApplicationEvent extends ApplicationEvent {

	private String name;
	
	public MyCustomApplicationEvent(Object source, String name) {
		super(source);
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}	
}


ApplicationEventPublisher Implementation

The publishEvent api in the ApplicationEventPublisher class is used to publish a spring boot application event. Create the application event object and provide it as a parameter to the publishEvent method. The publishEvent method sends data to all listener classes via the ApplicationEvent class. The publishEvent method and listeners runs asynchronously and finishes simultaneously.

TestController.java

package com.yawintutor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	@Autowired
	ApplicationEventPublisher publisher;
	
	@GetMapping(value = "/publish")
	public String publish() {
		System.out.println("TestController : publishing the message");

		MyCustomApplicationEvent event = new MyCustomApplicationEvent(this, "MyEvent");
		System.out.println("MyCustomApplicationEventPublisher : Source : "+this.getClass().getName() + ", Message : "+ event.getName());
		publisher.publishEvent(event);
		
		System.out.println("TestController : published the message");
		return "Published";
	}
}


Spring Boot Main Method

The spring boot main method will be called if the spring boot application starts. The spring boot main method will be shown as below.

SpringBootEventsApplication.java

package com.yawintutor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootEventsApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootEvents2Application.class, args);
	}
}


Pom.xml file

The pom.xml file for the spring boot events is provided below. The usage of the spring boot events does not necessitate any extra dependencies.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.yawintutor</groupId>
	<artifactId>SpringBootEvents</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootEvents</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


How to execute

Start the spring boot application and navigate to the URL shown below in the browser.

http://localhost:8080/publish



Leave a Reply