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.
The following 3 creations are required
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.
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.
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