preface


Spring provides the ApplicationContext event mechanism to publish and listen for events, which is a very useful feature.

Spring has built-in events and listeners. For example, when events occur before the Spring container starts, after the Spring container starts, or after the application fails to start, listeners listening on these events will respond accordingly.

Of course, we can also customize our own listeners to listen to Spring’s existing events. Or we can customize our own event and listener, publish the event at the necessary point in time, and then the listener listens to the event and responds.

ApplicationContext Event mechanism


ApplicationContext event mechanism adopts the observer design pattern to realize the event publishing and processing of ApplicationContext through the ApplicationEvent event class and ApplicationListener interface.

Whenever the ApplicationContext publishes an ApplicationEvent, the listener is triggered to perform the corresponding processing if there is an ApplicationListener Bean in the Spring container. Of course, the publication of the ApplicationEvent event needs to show the trigger, either Spring shows the trigger or we show the trigger.

ApplicationListener listener


Defines the interface that the application listener needs to implement. This interface inherits from the JDK standard EventListener interface, which is an empty token interface and is recommended for all event listeners to inherit from.

package org.springframework.context; import java.util.EventListener; @FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * */ void onApplicationEvent(E event); } 1.2.3.4.5.6.7.8.9.10.11.12.Copy the code
package java.util; Public interface EventListener {} 1.2.3.4.Copy the code

ApplicationListener is a generic interface. When we customize the implementation class of this interface, we will only listen for this event if we specify a specific event class of the generic type. If no concrete generics are specified, all subclasses of the ApplicationEvent abstract class are listened for.

Let’s define a listener that listens for specific events, such as ApplicationStartedEvent events.

package com.chenpi; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @slf44j @Component public Class MyApplicationListener  implements ApplicationListener<ApplicationStartedEvent> { @Override public void OnApplicationEvent (ApplicationStartedEvent Event) {log.info(">>> MyApplicationListener: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

Start the service and see that this listener is triggered after the service is started.

If no concrete generic class is specified, all subclass events of the ApplicationEvent abstract class are listened for. As follows:

package com.chenpi; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @slf44j @Component public Class MyApplicationListener  implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { log.info(">>> MyApplicationListener: {} ", the event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

Note that the listener class beans will not take effect unless they are injected into the Spring container. One is to use annotation injection, such as @Component. Also can use SpringApplicationBuilder. Listeners () method to add, but the two ways of distinguishing, look at the following example.

First, we use the @Component annotation to monitor two events when the service starts:

  • ApplicationStartedEvent
  • ApplicationReadyEvent
package com.chenpi; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.SpringApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @slf44j @Component public Class MyApplicationListener  implements ApplicationListener<SpringApplicationEvent> { @Override public void OnApplicationEvent (SpringApplicationEvent Event) {log.info(">>> MyApplicationListener: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

Using SpringApplicationBuilder. Listeners () method to add listeners, service starts, listening to the five events:

  • ApplicationEnvironmentPreparedEvent
  • ApplicationContextInitializedEvent
  • ApplicationPreparedEvent
  • ApplicationStartedEvent
  • ApplicationReadyEvent
package com.chenpi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) { // SpringApplication.run(Application.class, args); SpringApplication app = new SpringApplicationBuilder(Application.class) .listeners(new MyApplicationListener()).build();  app.run(args); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18}}.Copy the code

The @ Component annotated listener bean can only be used after the bean is initialized and registered. By SpringApplicationBuilder. Listeners () to add listeners bean is before the start of the container, so listen more to the event. However, do not use the two at the same time, or the listener will be repeated twice.

If you want to inject other beans (such as @autowired) into the listener bean, it is best to use the annotation form, because if you publish the listener too early, the other beans may not have been initialized and an error may be reported.

ApplicationEvent event


ApplicationEvent is an abstract class that all application events need to inherit from. It inherits from EventObject, which is the root class for all events, and this class has an Object of type Object source, which represents the source of events. Constructors of all classes that inherit from it must display and pass this event source.

package org.springframework.context; import java.util.EventObject; public abstract class ApplicationEvent extends EventObject { private static final long serialVersionUID = 7099057708183571937L; Private final long timestamp; public ApplicationEvent(Object source) { super(source); this.timestamp = System.currentTimeMillis(); } public final long getTimestamp() { return this.timestamp; 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20}}.Copy the code
package java.util; public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; protected transient Object source; public EventObject(Object source) { if (source == null) throw new IllegalArgumentException("null source"); this.source = source; } public Object getSource() { return source; } public String toString() { return getClass().getName() + "[source=" + source + "]"; 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23}}.Copy the code

One of the more important event classes in Spring is SpringApplicationEvent. Spring has built-in events that are fired when certain operations are completed. These built-in events inherit from the SpringApplicationEvent abstract class. SpringApplicationEvent extends ApplicationEvent and adds the string array parameter field args.

/** * Base class for {@link ApplicationEvent} related to a {@link SpringApplication}. * * @author Phillip Webb * @since 1.0.0 */ @SuppressWarnings("serial") public Abstract Class SpringApplicationEvent extends ApplicationEvent {private final String[] args; public SpringApplicationEvent(SpringApplication application, String[] args) { super(application); this.args = args; } public SpringApplication getSpringApplication() { return (SpringApplication) getSource(); } public final String[] getArgs() { return this.args; 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24}}.Copy the code

We can write our own listeners and then listen for these events to implement our own business logic. For example, write an implementation class for the ApplicationListener interface that listens for the ContextStartedEvent event, which will be published when the ApplicationContext is started, so the listener will be fired.

  • ContextRefreshedEvent: Events are published when the ApplicationContext is initialized or refreshed. ConfigurableApplicationContext interface the refresh () method is called will trigger event publishing. Initialization means that all beans are successfully loaded, post-processing beans are detected and activated, all singletons are pre-instantiated, and the ApplicationContext container is ready to be used.
  • ContextStartedEvent: Publish this event after the application context has been refreshed, but before any ApplicationRunner and CommandLineRunner are called.
  • ApplicationReadyEvent: This event is published as late as possible to indicate that the application is ready to service the request. The event source is the SpringApplication itself, but be careful to modify its internal state because all initialization steps will have been completed by then.
  • ContextStoppedEvent: ConfigurableApplicationContext interface of the stop () is called when they stop ApplicationContext events are released.
  • ContextClosedEvent: ConfigurableApplicationContext interface of the close () is called closed ApplicationContext, events are released. Note that once a closed context reaches the end of its lifecycle, it cannot be refreshed or restarted.
  • ApplicationFailedEvent: Publishes events when an application fails to be started.
  • ApplicationEnvironmentPreparedEvent: events are released when SpringApplication start, first check and change the Environment, and on the ApplicationContext has not been created.
  • ApplicationPreparedEvent: when the event is published, SpringApplication is starting and the ApplicationContext is fully prepared but not refreshed. At this stage, bean Definitions are loaded and the Environment is ready to be used.
  • RequestHandledEvent: This is a Web event that can only be applied to Web applications using DispatcherServlets. When using Spring as the FRONT-END MVC controller, the system automatically fires this event when Spring finishes processing a user request.

Custom events and listeners


You’ve already seen custom listeners, and then listening for Spring’s original events. Let’s introduce custom events and custom listeners, and then publish the event in the program, triggering the execution of the listener, and implementing your own business logic.

First, customize the event by inheriting ApplicationEvent. Of course, the event can customize its own properties.

package com.chenpi; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.springframework.context.ApplicationEvent; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @getter @setter public class MyApplicationEvent extends ApplicationEvent {private String myField; Public MyApplicationEvent(Object source, String myField) {// Bind event source super(source); this.myField = myField; } @Override public String toString() { return "MyApplicationEvent{" + "myField='" + myField + '\'' + ", source=" + source + '}'; 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31}}.Copy the code

We then define a custom listener to listen for our custom events.

package com.chenpi; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; /** * @description Custom listener * @author * @date 2021/6/26 * @version 1.0 */ @slf4j public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> { @Override public void onApplicationEvent(MyApplicationEvent event) {log.info(">>> MyApplicationListener: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18}}.Copy the code

Register listeners and publish events. There are two ways to register listeners as explained above. Events published by ApplicationEventPublisher. PublishEvent () method. Demo with configurableApplicationContext directly release here, it implements the ApplicationEventPublisher interface.

package com.chenpi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) { // SpringApplication.run(Application.class, args); SpringApplication app = New SpringApplicationBuilder(application.class).listeners(new) MyApplicationListener()).build(); ConfigurableApplicationContext configurableApplicationContext = app.run(args); / / convenient demonstration, after the project started publishing events, of course also can publish event in other operations, and other time points configurableApplicationContext. PublishEvent (new MyApplicationEvent (" I am the event source, Publish the event after the project starts successfully ", "I am a custom event attribute ")); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

Start the service and it shows that the published event is indeed listening.

The 16:15:09 2021-06-26. 10992-584 the INFO [main] O.S.B.W.E mbedded. Tomcat. TomcatWebServer: tomcat started on the port (s) : 8081 (HTTP) with context Path "2021-06-26 16:15:09.601 INFO 10992 -- [main] com.chenpi.Application: Started Application in 2.563 seconds (JVM running for 4.012) 2021-06-26 16:15:09.606 INFO 10992 -- [main] com.chenpi.MyApplicationListener : >>> MyApplicationListener: MyApplicationEvent{myField=' I am a custom event attribute ', source= I am an event source, publish the event after the project starts successfully}Copy the code

Event monitoring mechanism can achieve distribution and decoupling effect. For example, you can publish an event in a business class and let the listener listening on that event perform its own business processing. Such as:

package com.chenpi; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Service; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @service public class MyService implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void testEvent () {applicationEventPublisher. PublishEvent (new MyApplicationEvent (" I am the event source ", "I am a custom event attributes")); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27}}.Copy the code

Annotated listeners


In addition to implementing the ApplicationListener interface to create listeners, Spring also provides an annotation @EventListener to create listeners.

package com.chenpi; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * @description custom listener * @author * @date 2021/6/26 * @version 1.0 */ @slf4@component public class MyApplicationListener01 { @EventListener public void onApplicationEvent(MyApplicationEvent event) { log.info(">>> MyApplicationListener: {} ", the event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

Furthermore, annotations can be used to only listen for events of specified conditions through conditional filtering. For example, the event whose myField property value is equal to “tangerine peel” event.

package com.chenpi; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * @description custom listener * @author * @date 2021/6/26 * @version 1.0 */ @slf4@component public class MyApplicationListener01 {@eventListener (condition = "# event.myfield.equals (' peeler ')") public void OnApplicationEvent (MyApplicationEvent Event) {log.info(">>> MyApplicationListener: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21}}.Copy the code

You can also define more than one listener in the same class, and you can specify the order of different listeners for the same event. The smaller the order value, the more the order value is executed.

package com.chenpi; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * @description custom listener * @author * @date 2021/6/26 * @version 1.0 */ @slf4@component public class MyApplicationListener01 { @Order(2) @EventListener public void onApplicationEvent(MyApplicationEvent event) { Log.info (">>> onApplicationEvent order=2: {}", event); } @Order(1) @EventListener public void onApplicationEvent01(MyApplicationEvent event) { log.info(">>> onApplicationEvent The order = 1: {} ", the event); } @eventListener public void otherEvent(YourApplicationEvent event) {log.info(">>> otherEvent: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34}}.Copy the code

The execution result is as follows:

>>> onApplicationEvent order=1: MyApplicationEvent{myField=' ', source= ' '} >>> onApplicationEvent Order =2: MyApplicationEvent{myField=' peeler ', source= 'I am the source of the event'} >>> otherEvent: MyApplicationEvent{myField=' I am the custom event attribute 01', source= I am the event source 01}Copy the code

The listening processing of events is synchronous, as follows:

package com.chenpi; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Service; /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @service @slf4j public class MyService implements. /** * @description * @author * @date 2021/6/26 * @version 1.0 */ @service @slf4j public class MyService implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void testEvent() { log.info(">>> testEvent begin"); ApplicationEventPublisher. PublishEvent (new MyApplicationEvent (" I am the event source ", "orange")); ApplicationEventPublisher. PublishEvent (new YourApplicationEvent (" I am the event source 01 ", "I am a custom event attributes 01")); log.info(">>> testEvent end"); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31}}.Copy the code

The execution result is as follows:

2021-06-26 20:34:27.990 INFO 12936 -- [NIO-8081-exec-1] com.chenpi.MyService: > > > testEvent 20:34:27 begin 2021-06-26. 12936-990 the INFO [nio - 8081 - exec - 1]. Com chenpi. MyApplicationListener01: > > > onApplicationEvent order = 1: MyApplicationEvent {myField = 'dried tangerine or orange peel, Source = I'm 20:34:27 event source} 2021-06-26. 12936-991 the INFO [nio - 8081 - exec - 1]. Com chenpi. MyApplicationListener01: > > > onApplicationEvent order = 2: MyApplicationEvent {myField = 'dried tangerine or orange peel, Source = I'm 20:34:27 event source} 2021-06-26. 12936-992 the INFO [nio - 8081 - exec - 1]. Com chenpi. MyApplicationListener01: > > > otherEvent: MyApplicationEvent{myField=' I am custom event attribute 01', } 2021-06-26 20:34:27.992 INFO 12936 -- [niO-8081-exec-1] com.chenpi.MyService: > > > testEvent end 1.2.3.4.5.Copy the code

However, we can also specify an asynchronous way to execute the listener. Remember to enable the asynchronous annotation by adding the @enableAsync annotation to the service.

package com.chenpi; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * @description custom listener * @author * @date 2021/6/26 * @version 1.0 */ @slf4@component public class MyApplicationListener01 { @Async @Order(2) @EventListener public void onApplicationEvent(MyApplicationEvent event) { Log.info (">>> onApplicationEvent order=2: {}", event); } @Order(1) @EventListener public void onApplicationEvent01(MyApplicationEvent event) { log.info(">>> onApplicationEvent The order = 1: {} ", the event); } @async@eventListener public void otherEvent(YourApplicationEvent event) {log.info(">>> otherEvent: {}", event); 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38}}.Copy the code

The execution result is as follows. Note the printed thread name.

2021-06-26 20:37:04.807 INFO 9092 -- [NIO-8081-exec-1] com.chenpi.MyService: > > > testEvent 20:37:04 begin 2021-06-26. 9092-819 the INFO [nio - 8081 - exec - 1]. Com chenpi. MyApplicationListener01: > > > onApplicationEvent order = 1: MyApplicationEvent {myField = 'dried tangerine or orange peel, Source = I'm 20:37:04 event source} 2021-06-26. 9092-831 the INFO] [task - 1 com. Chenpi. MyApplicationListener01: > > > onApplicationEvent order = 2: MyApplicationEvent {myField = 'dried tangerine or orange peel, } 2021-06-26 20:37:04.831 INFO 9092 -- [NIO-8081-exec-1] com.chenpi.MyService: > > > testEvent end 20:37:04 2021-06-26. 9092-831 the INFO/task - 2 com. Chenpi. MyApplicationListener01: >>> otherEvent: MyApplicationEvent{myField=' I am the custom event attribute 01', source= I am the event source 01} 1.2.3.4.5.Copy the code