[Java] Steps required to issue asynchronous event of Spring Boot

3 minute read

Spring Framework provides a mechanism 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 for asynchronous processing. This time, I will describe an implementation example of asynchronous processing using asynchronous events in Spring Boot.

Operating environment

  • Java 11
  • SpringBoot 2.3.1

What you need for event-driven processing

The following three must be created

  • Event class that passes parameters necessary for processing execution
  • Publisher class that fires the event
  • Listener class that detects an event and starts processing

Implementation example

Event to be created this time: Receive a string from the calling class and output it to the log

Use the following for the class that stores the character string passed from the event.

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

Extends 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;
}

}

The first argument of the constructor: Object source is a mandatory implementation.

Publisher

Create a Java class that uses the org.springframework.context.ApplicationEventPublisher instance managed by Spring. Therefore, it is easy to add a class such as @Component or @Service and put it 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);
It's a sequel.
// create an event
PrimaryEvent event = new PrimaryEvent(this, sampleMessage);

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

Listener

Place it under Spring management 1 and implement org.springframework.context.ApplicationListener interface. The simplest is to add @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();
It's a sequel.
// Subsequent processing after receiving the message ↓......
}

}

The preparation for event execution is complete.

Execution example from Controller of Spring MVC

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 message");

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

This is the same as the implementation using Spring MVC, and Controller displays the screen after the event is executed and finished.

Make it asynchronous

To use asynchronous processing in SpringBoot, add @EnableAsync to the SpringBoot startup class and then add @Async to the class you want to use for 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, it is the following ignite method in Controller that you want to operate asynchronously.

DisplayController.java


public class DisplayController {

private final PrimaryEventPublisher publisher;

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

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

I want to run PrimaryEventPublisher asynchronously, so I 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 the process that can be executed can be output to the data source using @Repository, it can also be used to send some process completion notification to the in-house chat tool or email.[^1]: You can actually get it from Spring ApplicationContext, so it is not necessary to add annotation to put it under Spring management such as @Component. “Under Spring control = Class registered in ApplicationContext”. In Spring Boot, the component scan function that detects the existence of classes to which Spring annotations @Controller, @Service, @Repository, and @Component are added may be in a valid package. It is a premise.

  1. Because it is executed from ApplicationEventPublisher under Spring management. If you do not register it in ApplicationContext, it will not work (empty)