[JAVA] Steps required to issue an asynchronous event for Spring Boot

Spring Framework provides a mechanism that allows you to register the execution of an instance under Spring management as an "event" and control the execution by issuing an event. It's simple to use, and Spring Boot allows asynchronous processing. This time, I will describe an example of implementing asynchronous processing using asynchronous events in Spring Boot.

Operating environment

What you need for event-driven processing

The following 3 creations are required

Implementation example

Event to be created this time: Receives a character string from the calling class and outputs it to the log

The class that stores the character string passed from the event uses the following.

SampleMessage.java


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@AllArgsConstructor @Getter @ToString
public class SampleMessage {
	private String message;

	public static SampleMessage of(String message) {
		return new SampleMessage(message);
	}
}

Event

It inherits from org.springframework.context.ApplicationEvent.

PrimaryEvent.java


@Getter
public class PrimaryEvent extends ApplicationEvent {

	private final SampleMessage sampleMessage;

	public PrimaryEvent(Object source,  SampleMessage sampleMessage) {
		super(source);
		this.sampleMessage = sampleMessage;
	}

}

First argument of the constructor: Object source is a required implementation.

Publisher

Create a Java class using the org.springframework.context.ApplicationEventPublisher instance managed by Spring. Therefore, it is easy to add @Component or @Service to make it a class placed in Spring management [^ 1].

PrimaryEventPublisher.java


import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Component
@AllArgsConstructor
public class PrimaryEventPublisher {

	private final ApplicationEventPublisher applicationEventPublisher;

	public void ignite(String message) {
		SampleMessage sampleMessage = SampleMessage.of(message);
		
		//Create an event
		PrimaryEvent event = new PrimaryEvent(this, sampleMessage);

		//Event issuance!
		applicationEventPublisher.publishEvent(event);
	}
}

Listener

Place it under Spring management [^ 2] and implement the org.springframework.context.ApplicationListener interface. The simplest is to give @Component.

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j2;

@Component
public class PrimaryEventListener implements ApplicationListener<PrimaryEvent>{

	@Override
	public void onApplicationEvent(PrimaryEvent event) {
		SampleMessage sampleMessage = event.getSampleMessage();
		
		//Subsequent processing after receiving a message ↓......
	}

}

This completes the preparation for event execution.

Execution example from Spring MVC Controller

This is an example of receiving a request in Controller, executing an event, and then displaying the screen.

DisplayController.java


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import lombok.AllArgsConstructor;

@RequestMapping("/display")
@Controller
@AllArgsConstructor
public class DisplayController {

	private final PrimaryEventPublisher publisher;

	@GetMapping
	public ModelAndView display(ModelAndView mnv) {

		publisher.ignite("Send a message");

		mnv.setViewName("display");
		return mnv;
	}
}

This is no different from the implementation using Spring MVC, and the Controller displays the screen after the event is executed and finished.

Asynchronous processing

To use asynchronous processing in SpringBoot, give @EnableAsync to the SpringBoot startup class, and then add @Async to the class you want to make asynchronous processing.

SpringEventSampleApplication.java


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class SpringEventSampleApplication {

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

}

For example, what you want to operate asynchronously is the following ignite method in Controller.

DisplayController.java


public class DisplayController {

	private final PrimaryEventPublisher publisher;

	@GetMapping
	public ModelAndView display(ModelAndView mnv) {
		//I want to process it separately from the screen display
		publisher.ignite("Send a message");

		mnv.setViewName("display");
		return mnv;
	}
}

I want to run PrimaryEventPublisher asynchronously, so just add @ Async.

PrimaryEventPublisher.java


import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import lombok.AllArgsConstructor;

@Component
@AllArgsConstructor
@Async
public class PrimaryEventPublisher {

	private final ApplicationEventPublisher applicationEventPublisher;

	public void ignite(String message) {
		SampleMessage sampleMessage = SampleMessage.of(message);

		PrimaryEvent event = new PrimaryEvent(this, sampleMessage);
		applicationEventPublisher.publishEvent(event);
	}
}

It's very easy d (・ ω ・ ・ Since it is possible to output to the data source using @Repository for the processing that can be executed, it can also be used for sending some processing completion notification to the in-house chat tool or email.

[^ 1]: Actually, it can be obtained from Spring ApplicationContext, so it is not necessary to add annotations such as @Component to put it under Spring management. "Spring managed = class registered in ApplicationContext". In Spring Boot, the ComponentScan function that detects the existence of classes with the Spring annotations @Controller, @Service, @ Repository, and @Component must be in a valid package. It is a premise.

[^ 2]: Because it is executed from ApplicationEventPublisher under Spring management. If you do not register it in ApplicationContext, it will not work (it will be missed)

Recommended Posts

Steps required to issue an asynchronous event for Spring Boot
Spring.messages.fallback-to-system-locale: false is required to default message.properties for i18n support in Spring boot
An introduction to Spring Boot + in-memory data grid
Plans to support JDK 11 for Eclipse and Spring Boot
Settings for connecting to MySQL with Spring Boot + Spring JDBC
How to set Dependency Injection (DI) for Spring Boot
How to write a unit test for Spring Boot 2
[Spring Boot] How to create a project (for beginners)
From building an AWS cloud environment to deploying a Spring Boot app (for beginners)
Try Spring Boot from 0 to 100.
Introduction to Spring Boot ① ~ DI ~
Introduction to Spring Boot ② ~ AOP ~
[Spring Boot] Send an email
Introduction to Spring Boot Part 1
Spring Boot for annotation learning
[Spring Boot] What to do if an HttpMediaTypeNotAcceptableException occurs on an endpoint for which produces is set
How to make a hinadan for a Spring Boot project using SPRING INITIALIZR
02. I made an API to connect to MySQL (MyBatis) from Spring Boot
Processing to issue an error message
Spring Boot for the first time
Create an app with Spring Boot 2
How to set Spring Boot + PostgreSQL
Frequent annotations for Spring Boot tests
Create an app with Spring Boot
Use DBUnit for Spring Boot test
How to use ModelMapper (Spring boot)
Upgrade spring boot from 1.5 series to 2.0 series
Workaround for Command Line Runner to work with JUnit in Spring Boot
[For internal use] For those assigned to the Spring Boot project (under construction)
Steps to create a simple camel app using Apache Camel Spring Boot starters
[Introduction to Spring Boot] Form validation check
Story when moving from Spring Boot 1.5 to 2.1
Changes when migrating from Spring Boot 1.5 to Spring Boot 2.0
WebMvcConfigurer Memorandum of Understanding for Spring Boot 2.0 (Spring 5)
Changes when migrating from Spring Boot 2.0 to Spring Boot 2.2
Asynchronous processing with Spring Boot using @Async
How to split Spring Boot message file
Add spring boot and gradle to eclipse
[Error resolution] Occurs when trying to build an environment for spring with docker
When you want to notify an error somewhere when using graphql-spring-boot in Spring Boot
From creating a Spring Boot project to running an application with VS Code