Interview begins:

Interviewer: You are 85 years old

Me: Well, 35

Interviewer: That must be a lot of experience. Let’s talk about Spring

I: good, this I used 10 years, you casually ask

Interviewer: Have you used events in Spring?

I: used

Interviewer: Can you explain why you need to use events?

Me: Using the event pattern can decouple the system, the event source publishes an event, the event listener can consume the event, and the event source does not care about the listener of the published event, which can decouple the system

Interviewer: How many ways can Spring events be implemented?

Me: Generally speaking, there are two ways. The first way is through the interface, and the second way is using annotations in the method

Interviewer: Are event listeners handled synchronously or asynchronously in Spring?

Me: Sorry, I didn’t understand the question

Interviewer: Do event publishing and event listener execution run in the same thread?

Me: Executing in a thread is synchronous

Interviewer: Do you support asynchronous mode?

I: support

Interviewer: Are you sure?

Me: HMM… The logic in the event listener is usually not the primary business and can not be executed in the current thread.

Interviewer: Do spring event listeners support custom ordering?

Me: I don’t know

Interviewer: ok, today’s interview so far, after going back to consolidate their own technology, look at the source code, do not waste, otherwise it will be more and more difficult

Me: Ok… The brain is already a paste.

After going back to spring events this piece of source code out and carefully studied several times.

The main questions in the interview process

  1. Why use the event pattern?
  2. How many ways can you implement events in Spring?
  3. Does spring support asynchronous mode for event listener consumption events?
  4. Does spring support custom ordering of event listener consumption events?

Let’s introduce them one by one.

Why use the time pattern?

Let’s start with a business scenario:

Product manager: Passer-by, these two days you help me to achieve a registration function

Me: The registration function is relatively simple, the user information into the library can be, pseudo code is as follows:

Public void registerUser(UserModel user){this.insertUser(user); }Copy the code

After a few days, product manager: passer-by, after the success of registration, to the user to send a successful email registration

I: modified the above registration code, as follows:

Public void registerUser(UserModel user){this.insertUser(user); // Send a message this.sendemailtouser (user); }Copy the code

Because of the changes to the registration interface, all calls to this method need to be retested, let the test brothers help run through.

A few more days passed

Product manager: Passer-by, after successful registration, give users a coupon

Me: Ok, adjusted the code again

Public void registerUser(UserModel user){this.insertUser(user); // Send a message this.sendemailtouser (user); // Send coupon this.sendCouponToUser(user); }Copy the code

I: test brothers, laborious everyone, registration interface has been modified, help again.

After a period of time, the company benefits too good

Product manager: Passerby, when registering, cancel the function of sending coupons to users.

Me: I went to adjust the above code again and disabled the function of sending coupons, as follows

Public void registerUser(UserModel user){this.insertUser(user); // Send a message this.sendemailtouser (user); }Copy the code

Because the code has been adjusted, and the registration function is core business, we need to ask the test to help again, and we need to bother the test again.

And then one day

Product manager: Passerby, how come it takes so long to register the interface and it often fails? You’re costing the company a lot of users

Me: I hurried to check the run log, and found that it was unstable to send emails to users when registering, and it depended on the third-party mail server, which took a long time and was easy to fail.

Go to the product manager and say: Registration is not stable because the mail server is not stable.

Product Manager: You don’t have to send emails, but you have to make sure the registration function works.

I thought about it and changed the above code to the following, sending mail is executed in the child thread:

Public void registerUser(UserModel user){this.insertUser(user); New Thread(()->{this.sendeMailtouser (user); }).start(); }Copy the code

A few more days passed

The product manager ran and said: passers-by, the recent benefit is not good, need to stimulate the user consumption, registration of the time to continue to send coupons.

I: pour, this is to play me, repeatedly let me adjust the registration of the code, let me change good, let the test also repeatedly do back and forth, this is to play dead us.

Took some time to do a thorough review:

Finding that the problem is not with the product manager:

  1. From the business point of view, the product raised these requirements are reasonable requirements, and the result of repeated code adjustments, repeated testing, and some minor functions caused the registration interface instability.
  2. In the final analysis, these problems are mainly caused by my unreasonable design. Some secondary functions in the registration function are coupled to the registration method, and these functions may be adjusted frequently, resulting in the instability of the registration interface.

The code above can do this:

Find three people: registrar, passer-by A, and passer-by B.

Register: responsible for the user information fall, fall after the success of the library, shout: user XXX registered successfully.

Passer-by A and passer-by B, listen up, when they hear someone shout: XXX registration success, immediately take action and make the following response:

Passerby A: Responsible for sending A registration email to XXX

Passerby B: Responsible for sending coupons to XXX

Let’s take a look:

The registry is only responsible for depositing user information and broadcasting a successful user registration message.

As A listener, A and B only listen for the message of user registration success. When they hear the message, A and B do their own work.

In this case, the registrar can not sense the existence of A/B, and A and B do not need to sense the existence of the registrar. A/B only pays attention to whether someone broadcasts the message that XXX is successfully registered. When AB hears the message that XXX is successfully registered, they react and rest idle.

This is great:

When you don’t want to send coupons to users, you just need to remove THE B. At this time, you basically don’t need to test, just register the code of B.

If after registration need more business, such as the need to increase integral to the user, only need to add a listener C, after listening to the registered success message, responsible for users to add points and didn’t even need to adjust the registration code, developers and testers only need to make sure that the listener in C is correct.

The above pattern is the event pattern.

Several concepts in the event pattern

Event sources: Event triggers, such as the registry above, are event sources.

Event: An object that describes what happened, such as the above: XXX registration success event

Event listener: listen to the event when it happens, do some processing, such as the above: passerby A, passerby B

Now we use the event pattern to implement the business of user registration

Let’s start by defining a few classes that are related to events.

The event object

Represents the parent class of all events, with a source field inside, representing the event source; Our custom events need to inherit from this class.

package com.javacode2018.lesson003.demo1.test0.event; Public abstract class AbstractEvent {// Event source protected Objectsource;
 
    public AbstractEvent(Object source) {
        this.source = source;
    }
 
    public Object getSource() {
        return source;
    }
 
    public void setSource(Object source) {
        this.source = source; }}Copy the code

Event listener

We use an interface to represent the event listener. It is a generic interface, followed by type E, which represents the type of event the listener needs to listen for. There is only one method in this interface, which implements the business of handling events. It defines listeners that need to implement this interface.

package com.javacode2018.lesson003.demo1.test0.event; Public interface EventListener<E extends AbstractEvent> {/** * this method is responsible for handling events * * @param event The event object to respond to */ void onEvent(E event); }Copy the code

Event broadcaster

Manage event listeners (register listeners & remove listeners, associate events with listeners)

Responsible for event broadcast (broadcast the event to all listeners, and listeners interested in the event will process the event)

package com.javacode2018.lesson003.demo1.test0.event; /** * event listeners: * 1. Manage event listeners (register listeners & remove listeners, associate events with listeners) * 2. Public interface EventMulticaster {/** * Broadcast events to all listeners, Listeners interested in this event will process it * * @param event */ void multicastEvent(AbstractEvent Event); /** * add an EventListener ** @param listener need to add listener */ void addEventListener(EventListener<? > listener); ** @param Listener The listener to be removed */ void removeEventListener(EventListener<? > listener); }Copy the code

Event broadcast implemented by default

package com.javacode2018.lesson003.demo1.test0.event; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; */ Public class SimpleEventMulticaster implements EventMulticaster {private Map< class <? >, List<EventListener>> eventObjectEventListenerMap = new ConcurrentHashMap<>(); @Override public void multicastEvent(AbstractEvent event) { List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(event.getClass());if(eventListeners ! = null) {for(EventListener eventListener : eventListeners) { eventListener.onEvent(event); } } } @Override public void addEventListener(EventListener<? > listener) { Class<? > eventType = this.getEventType(listener); List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType);if(eventListeners == null) { eventListeners = new ArrayList<>(); this.eventObjectEventListenerMap.put(eventType, eventListeners); } eventListeners.add(listener); } @Override public void removeEventListener(EventListener<? > listener) { Class<? > eventType = this.getEventType(listener); List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType);if(eventListeners ! = null) { eventListeners.remove(listener); }} /** * gets the type of event that the event listener needs to listen for ** @param listener * @return*/ protected Class<? > getEventType(EventListener listener) { ParameterizedType parameterizedType = (ParameterizedType) listener.getClass().getGenericInterfaces()[0]; Type eventType = parameterizedType.getActualTypeArguments()[0];return (Class<?>) eventType;
    }
 
}Copy the code

The above three classes support the whole time model. Now we use the above three classes to realize the function of registration. The goal is: high cohesion and low coupling, so that the registration logic is easy to expand.

Custom user registration success event class

Inheriting the AbstractEvent class

package com.javacode2018.lesson003.demo1.test0.userregister; import com.javacode2018.lesson003.demo1.test0.event.AbstractEvent; /** * public class UserRegisterSuccessEvent extends AbstractEvent {private String userName; /** * Create user registration success event object ** @paramsourceEvent source * @param userName current registered userName */ public UserRegisterSuccessEvent(Object)source, String userName) {
        super(source);
        this.userName = userName;
    }
 
    public String getUserName() {
        return userName;
    }
 
    public void setUserName(String userName) { this.userName = userName; }}Copy the code

User Registration Service

Responsible for implementing user registration logic

package com.javacode2018.lesson003.demo1.test0.userregister; import com.javacode2018.lesson003.demo1.test0.event.EventMulticaster; /** * User registration service */ public class UserRegisterService {// Event issuer private EventMulticaster EventMulticaster; Public void registerUser(String userName) {//@0 /** * @param userName */ public void registerUser(String userName) { System.out.println(String.format("User [%s] registered successfully", userName)); This. / / @ / / 2 broadcast events eventMulticaster. MulticastEvent (new UserRegisterSuccessEvent (this userName)); //@3 } public EventMulticastergetEventMulticaster() {
        return eventMulticaster;
    }
 
    public void setEventMulticaster(EventMulticaster eventMulticaster) { this.eventMulticaster = eventMulticaster; }}Copy the code

@0: Event publisher

@1: registerUser This method is responsible for user registration and does two main things internally

@2: Simulate to drop user information into the database

@3: Use eventPublisher to publish user registration success:

Let’s use Spring to assemble the above objects

package com.javacode2018.lesson003.demo1.test0.userregister; import com.javacode2018.lesson003.demo1.test0.event.EventListener; import com.javacode2018.lesson003.demo1.test0.event.EventMulticaster; import com.javacode2018.lesson003.demo1.test0.event.SimpleEventMulticaster; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import java.util.List; @configuration @ComponentScan public class MainConfig0 {/** * Register a bean: event listener ** @param eventListeners * @return
     */
    @Bean
    @Autowired(required = false)
    public EventMulticaster eventMulticaster(List<EventListener> eventListeners) { //@1
        EventMulticaster eventPublisher = new SimpleEventMulticaster();
        if(eventListeners ! = null) { eventListeners.forEach(eventPublisher::addEventListener); }returneventPublisher; } /** * Register a bean: User registration service ** @param eventMulticaster * @return
     */
    @Bean
    public UserRegisterService userRegisterService(EventMulticaster eventMulticaster) { //@2
        UserRegisterService userRegisterService = new UserRegisterService();
        userRegisterService.setEventMulticaster(eventMulticaster);
        returnuserRegisterService; }}Copy the code

There are two methods that register two beans in the Spring container.

@1: Registers a bean with the Spring container: event publisher. The method passes in a List of type EventListener, which will inject all the event listeners in the container into EventMulticaster.

@2: Registered a bean with the Spring container: user registration service

Let’s simulate user registration with a test case

package com.javacode2018.lesson003.demo1;
 
import com.javacode2018.lesson003.demo1.test0.userregister.MainConfig0;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class EventTest {
 
    @Test
    public void test0() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig0.class); / / get the user registration service com. Javacode2018. Lesson003. Not. Test0. Userregister. UserRegisterService UserRegisterService = context.getBean(com.javacode2018.lesson003.demo1.test0.userregister.UserRegisterService.class); / / simulated user registration userRegisterService. RegisterUser ("Passer-by Java"); }}Copy the code

Run the output

User [pass-by Java] registered successfully

Added the function of sending emails after registration

Add a function to send email with successful registration, just need to define a listener to listen to the user registration success event, the other code does not need any changes, as follows

package com.javacode2018.lesson003.demo1.test0.userregister; import com.javacode2018.lesson003.demo1.test0.event.EventListener; import org.springframework.stereotype.Component; / * * * user registration successful event listener - > to send mail to users * / @ Component public class SendEmailOnUserRegisterSuccessListener implements EventListener<UserRegisterSuccessEvent> { @Override public void onEvent(UserRegisterSuccessEvent event) { System.out.println( String.format("Send user [%s] registration success email!", event.getUserName())); }}Copy the code

The above class uses @Component and is automatically scanned and registered with the Spring container.

Run the test case output again

User [pass-by Java] registered successfully

To the user [passer a Java] to send a successful email registration!

summary

This decouples the primary logic of registration (user information dropping) from the secondary business logic (mail sending) via events. Minor services are made pluggable, such as the @Component annotation on the mail listener that you don’t want to send.

The above use and the event related several classes, are our own implementation, in fact, these functions in spring has helped us to achieve a good, easier to use, let us experience.

Implement the event pattern in Spring

Several event-related classes

Spring event-related classes need to be understood first, the following table, the Spring event-related classes and our above custom class to do a comparison, convenient for everyone to understand

These classes and our custom class code is a bit similar, interested can go to see the source code, here will not be listed.

Hard-coded approach using spring event 3 step

Step 1: Define events

Custom events that extend from the ApplicationEvent class,

Step 2: Define listeners

To customize event listeners, you need to implement the ApplicationListener interface, which has a method onApplicationEvent that needs to be implemented to handle events of interest.

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
 
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
 
}Copy the code

Step 3: Create an event broadcaster

Create an event broadcast ApplicationEventMulticaster, this is an interface, you can implement this interface, also can use the system to provide us with SimpleApplicationEventMulticaster directly, as follows:

ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();

Step 4: Register event listeners with the broadcaster

Event listener registered to ApplicationEventMulticaster radio device, such as:

ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();

applicationEventMulticaster.addApplicationListener(new SendEmailOnOrderCreateListener());

Step 5: Publish events through broadcasters

Radio broadcast events, call ApplicationEventMulticaster# multicastEvent method, the radio apparatus are interested in this event listener will handle this issue.

applicationEventMulticaster.multicastEvent(new OrderCreateEvent(applicationEventMulticaster, 1L));

Let’s use an example to put these five steps together.

case

Implementation function: after the order is successfully created in the e-commerce, send an email to the next person. The function of sending emails is realized in the listener.

So here’s the code

Here’s an event class: order creation success event

package com.javacode2018.lesson003.demo1.test1; import org.springframework.context.ApplicationEvent; Public class OrderCreateEvent extends ApplicationEvent {// orderId private Long orderId; /** * @paramsource@param orderId orderId */ public OrderCreateEvent(Objectsource, Long orderId) {
        super(source);
        this.orderId = orderId;
    }
 
    public Long getOrderId() {
        return orderId;
    }
 
    public void setOrderId(Long orderId) { this.orderId = orderId; }}Copy the code

Have a listener: responsible for listening for order success events, send mail

package com.javacode2018.lesson003.demo1.test1; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; / * * * order creation success to email users * / @ Component public class SendEmailOnOrderCreateListener implements ApplicationListener<OrderCreateEvent> { @Override public void onApplicationEvent(OrderCreateEvent event) { System.out.println(String.format("Order [%d] created successfully, send email notification to the next person!", event.getOrderId())); }}Copy the code

The test case

@Test
public void test(2) throws InterruptedException {/ / create an event broadcast ApplicationEventMulticaster ApplicationEventMulticaster = new SimpleApplicationEventMulticaster(); / / register event listeners applicationEventMulticaster. AddApplicationListener (new SendEmailOnOrderCreateListener ()); . / / broadcast order creation events applicationEventMulticaster multicastEvent (new OrderCreateEvent (applicationEventMulticaster, 1 l)); }Copy the code

Run the output

Order [1] created successfully, send email notification to the next person!

Support for events in the ApplicationContext container

The above example demonstrates the use of events in Spring, so we usually use this when using Spring?

No, no, no, no, no, no, no, no, no, no, no, no, no, no, no.

Typically, we use classes ending in ApplicationContext as spring containers to launch applications. The following two are common

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext

Look at a class diagram

Let’s explain this picture:

1. AnnotationConfigApplicationContext and ClassPathXmlApplicationContext inherited AbstractApplicationContext 2. AbstractApplicationContext ApplicationEventPublisher interface is realized 3. A ApplicationEventMulticaster AbstractApplicationContext internal type fieldCopy the code

Article 3 above, illustrates the AbstractApplicationContext internal has integrated event broadcast ApplicationEventMulticaster, is given to illustrate the specific events AbstractApplicationContext internal related functions, These functions are implemented through its internal ApplicationEventMulticaster, that is to say, the function of the events entrusted to the internal ApplicationEventMulticaster to implementation.

ApplicationEventPublisher interface

The class diagram above more than a new interface ApplicationEventPublisher, take a look at the source code

@FunctionalInterface
public interface ApplicationEventPublisher {
 
    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }
 
    void publishEvent(Object event);
 
}Copy the code

This interface is used to publish events, and internally defined 2 methods are used to publish events.

There isn’t a ApplicationEventMulticaster interface yao in the spring, how again come here a publish event interface?

This interface implementation class, such as AnnotationConfigApplicationContext internal entrust this two methods ApplicationEventMulticaster# multicastEvent for processing.

The so called AbstractApplicationContext publishEvent method, in order to achieve the effect of the broadcast events, but use AbstractApplicationContext also can only by calling publishEvent methods to broadcast events.

Get ApplicationEventPublisher object

If we want to get ApplicationEventPublisher object, in the common bean needs to implement ApplicationEventPublisherAware interface

public interface ApplicationEventPublisherAware extends Aware {
    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
}Copy the code

The spring container automatically through the above setApplicationEventPublisher method will ApplicationEventPublisher injection to come in, we can use this to publish events.

To simplify the use of events, Spring provides two ways to use them

  1. Interface mode
  2. Face @eventListener annotation method

Interface mode

case

To achieve the user registration after the successful release of events, and then in the listener to send mail function.

User Registration Event

You need to inherit ApplicationEvent

package com.javacode2018.lesson003.demo1.test2; import org.springframework.context.ApplicationEvent; /** * public class UserRegisterEvent extends ApplicationEvent {private String userName; public UserRegisterEvent(Objectsource, String userName) {
        super(source);
        this.userName = userName;
    }
 
    public String getUserName() {
        returnuserName; }}Copy the code

Send mail listeners

The ApplicationListener interface needs to be implemented

package com.javacode2018.lesson003.demo1.test2; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @component public class SendEmailListener implements ApplicationListener<UserRegisterEvent> { @Override public void onApplicationEvent(UserRegisterEvent event) { System.out.println(String.format("Send user [%s] registration success email!", event.getUserName())); }}Copy the code

User Registration Service

Internally provides the function of user registration and publishes user registration events

package com.javacode2018.lesson003.demo1.test2; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; / * * * * user registration service / @ Component public class UserRegisterService implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher; ** @param userName */ public void registerUser(String userName) {public void registerUser(String userName) { System.out.println(String.format("User [%s] registered successfully", userName)); / / release registration event this. ApplicationEventPublisher. PublishEvent (new UserRegisterEvent (this userName)); } @Override public voidsetApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { //@1 this.applicationEventPublisher = applicationEventPublisher; }}Copy the code

Note that implements the above ApplicationEventPublisherAware interface, the spring container will pass @ 1 will ApplicationEventPublisher injection to come in, and then we can use this to publish events.

Let’s do a Spring configuration class

package com.javacode2018.lesson003.demo1.test2;
 
import org.springframework.context.annotation.ComponentScan;
 
@ComponentScan
public class MainConfig2 {
}Copy the code

Test cases

@Test
public void test2() throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();  context.register(MainConfig2.class); context.refresh(); / / get the user registration service com. Javacode2018. Lesson003. Not. Test2. UserRegisterService UserRegisterService = context.getBean(com.javacode2018.lesson003.demo1.test2.UserRegisterService.class); / / simulated user registration userRegisterService. RegisterUser ("Passer-by Java");
}Copy the code

Run the output

User [pass-by Java] registered successfully

To the user [passer a Java] to send a successful email registration!

The principle of

During bean creation, the Spring container determines whether the bean is of type ApplicationListener, And then to see it as a listener registered to AbstractApplicationContext# applicationEventMulticaster, the source code in the following method, interested can look at

org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitializationCopy the code

summary

As can be seen from the above example, the event class and listener class are based on the spring event related interface to implement the function of the event, which is called the face interface approach.

Face @eventListener annotation method

usage

Spring also provides the @EventListener annotation to create a listener. Annotate the annotation directly to a bean method. This method can then be used to handle events of interest. Method parameter type is the type of the event:

@Component
public class UserRegisterListener {
    @EventListener
    public void sendMail(UserRegisterEvent event) {
        System.out.println(String.format("Send user [%s] registration success email!", event.getUserName())); }}Copy the code

case

Once registered, there are two listeners: one to send mail and one to send coupons.

As in the above example, we will focus on the listener code as follows:

package com.javacode2018.lesson003.demo1.test3; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * UserRegisterListener */ @component public class UserRegisterListener {@eventlistener public void sendMail(UserRegisterEvent event) { System.out.println(String.format("Send user [%s] registration success email!", event.getUserName()));
    }
 
    @EventListener
    public void sendCompon(UserRegisterEvent event) {
        System.out.println(String.format("Send coupons to users [%s]!", event.getUserName())); }}Copy the code

This case code

com.javacode2018.lesson003.demo1.EventTest#test3Copy the code

The results

User [pass-by Java] registered successfully

Send coupons to users [pedestrian A Java]!

To the user [passer a Java] to send a successful email registration!

The principle of

The source code for handling the @EventListener annotation in Spring is in the method below

org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiatedCopy the code

EventListenerMethodProcessor SmartInitializingSingleton interface is achieved, SmartInitializingSingleton afterSingletonsInstantiated method of interface will after all singleton beans to create is called the spring container, this can go to have a look at: the content of the bean, a life cycle

Idea supports annotations better

Idea supports this better. A headset will be displayed at the post time. When clicking on the headset, Spring will list the listeners for the event

Clicking on the earphone lists 2 listeners that can be quickly located to the listeners, as shown below

There is also a broadcast icon in the listener, as shown below

Click on the broadcast icon above to quickly navigate to where the event is posted, which is quite convenient.

Listeners support sorting

If an event has more than one listener, the order in which the listeners are executed is unordered by default, although we can specify the order for the listeners.

Implement the listener case through the interface

If custom listeners are implemented through the ApplicationListener interface, there are three ways to specify the order of listeners

Method 1: org. Springframework. Core. Ordered interface

You need to implement a getOrder method that returns the sequential value, the smaller the value, the higher the order

int getOrder();Copy the code

Method 2: org. Springframework. Core. PriorityOrdered interface

The PriorityOrdered interface inherits the Ordered interface from Method 1, so if you implement the PriorityOrdered interface, you need to implement the getOrder method as well.

Method 3: class using the @ org. Springframework. Core. The annotation. The Order notes

Take a look at the source code for this annotation

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
 
    int value() default Ordered.LOWEST_PRECEDENCE;
 
}Copy the code

The value attribute is used to specify the order

There are several ways to sort rules

PriorityOrdered#getOrder ASC,Ordered or @order ASCCopy the code

Implement the EventListener case via @eventlistener

You can annotate the Order with the @Order annotation above the @EventListener annotation, as in:

@EventListener
@Order(1)
public void sendMail(com.javacode2018.lesson003.demo1.test3.UserRegisterEvent event) {
    System.out.println(String.format("Send user [%s] registration success email!", event.getUserName()));
}Copy the code

case

package com.javacode2018.lesson003.demo1.test4; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @component public class UserRegisterListener {@eventListener @order (1) public void sendMail(UserRegisterEvent event) { System.out.println(String.format("[%s], send registration success email to user [%s]!", Thread.currentThread(), event.getUserName()));
    }
 
    @EventListener
    @Order(0)
    public void sendCompon(UserRegisterEvent event) {
        System.out.println(String.format("[%s], send coupons to users [%s]!", Thread.currentThread(), event.getUserName())); }}Copy the code

Coupons are sent first, followed by an email.

Incidentally, thread information is also printed in the above output.

Corresponding test cases

com.javacode2018.lesson003.demo1.EventTest#test4Copy the code

Run the output

Thread[main,5,main]

Thread[main,5,main] : Thread[main,5,main] : Thread[main,5,main]

【Thread[main,5,main]】

Can be seen from the output of the above execution are performed for the main thread, the event listener registered logic and the logic in a thread of execution, at this time if the listener logic in the more time-consuming or failure, will lead to the failure to register directly, usually we put some main logic can not performed the listener, as for the success or failure of main logic, It is best not to affect the main logic, so it is best to separate the execution of the listener from the main business and execute it in a different thread. The main business does not care about the results of the listener, which is supported in Spring.

Listener asynchronous mode

So how do we do that?

Listener is ultimately through ApplicationEventMulticaster internal implementation to call, so we focus on is the class, this class has a default implementation class SimpleApplicationEventMulticaster, this class is to support the listener asynchronous calls, There is a field in it:

private Executor taskExecutor;Copy the code

The Executor interface is familiar to those who are familiar with high concurrency and can be used to perform tasks asynchronously.

We often use the thread pool class Java. Util. Concurrent. ThreadPoolExecutor is realized the Executor interface.

Look at the SimpleApplicationEventMulticaster event listeners in the call, will perform the following this approach

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type= (eventType ! = null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor();for(ApplicationListener<? > listener : getApplicationListeners(event,type)) {
        if(executor ! = null) { //@1 executor.execute(() -> invokeListener(listener, event)); }else{ invokeListener(listener, event); }}}Copy the code

If the executor is not empty, the listener will be called asynchronously. If the executor is not empty, the listener will be called asynchronously. By default, executors are empty. Now we need to set a value to it, and now we need to look at how the broadcast is created in the container, and we’re going to intervene there.

We usually use container is AbstractApplicationContext type, need to have a look at how AbstractApplicationContext radio apparatus is initialization, is under this method, container startup time is called, Used to initialize the AbstractApplicationContext events in broadcasting applicationEventMulticaster

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
 
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    }
    else{ this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); }}Copy the code

To explain the above logic: Whether in the spring container called applicationEventMulticaster famous bean, if you have it as the event broadcast, or create a SimpleApplicationEventMulticaster as radio apparatus, And register it with the Spring container.

It can be concluded from the above that: We only need a custom type for SimpleApplicationEventMulticaster name for applicationEventMulticaster bean is ok, by the way to the executor to set a value, can realize the listener asynchronous execution.

The concrete implementation is as follows

package com.javacode2018.lesson003.demo1.test5;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
 
import java.util.concurrent.Executor;
 
@ComponentScan
@Configuration
public class MainConfig5 {
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {/ / @ / 1 / create an event broadcast SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster (); / / to the radio, to provide a thread pool, through the thread pool to invoke the event listener Executor Executor = this. ApplicationEventMulticasterThreadPool (). The getObject (); // Set the asynchronous executor result.settAskExecutor (executor); / / @ 1return result;
    }
 
    @Bean
    public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
        ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
        result.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
        result.setCorePoolSize(5);
        returnresult; }}Copy the code

@ 1: define a name for the event applicationEventMulticaster radio apparatus, set up an internal thread pool is used for asynchronous invocation listeners

This code corresponds to the test case

com.javacode2018.lesson003.demo1.EventTest#test5Copy the code

Run the output

Thread[main,5,main]

The current Thread (Thread [applicationEventMulticasterThreadPool – 2, 5, the main]], to the user sends a stranger a Java 】 【 registered mail success!

The current Thread (Thread [applicationEventMulticasterThreadPool – 1, 5, the main]], give the user the stranger a Java 】 some coupons!

At this point, the effect of asynchronous listener execution is achieved.

Suggestions on event usage

  1. Do events in Spring use interfaces or annotations? It doesn’t matter which way you use it, but within the company it’s best for everyone to use the same way
  2. The pattern of asynchronous events usually places non-major business execution in listeners, which need to be used with care because of the risk of failure. If it is only for decoupling, but the decoupled secondary business must also succeed, message-oriented approaches can be used to address these issues.
  3. This is the end of the event. If you have any questions, please leave a comment.

The original link: https://blog.csdn.net/weixin_45850766/article/details/106083436