Spring Master's Road 7 - Comprehensive exploration of event mechanism and listeners

1. Observer pattern in Spring

  The Observer pattern is a behavioral design pattern that defines dependencies between objects. When the state of an object changes, all objects that depend on it will be notified and automatically updated. In this pattern, the object that changes state is called the subject, and the dependent objects are called observers.

Give a practical example:

  1. Event Source : Can be regarded as a "topic ( Subject)". When its status changes (such as playing new content), all observers will be notified. Imagine we are listening to the radio. A radio station is an event source that provides a large amount of news, music, and other content.

  2. Event : This is a specific representation of a change in subject status, which corresponds to the broadcast example, which is news, music and other content. Every time the radio broadcasts new content, it is equivalent to a new event being released.

  3. Broadcaster (Event Publisher / Multicaster) : The broadcaster plays the role of a mediator, which delivers events from the event source to the listener. In this example, the broadcast tower fills this role by sending the radio signal of the station's program into the air so that a radio receiver (listener) can pick it up.

  4. Listener : Listeners are "observers" who listen and respond to specific events. In this example, a radio receiver is a monitor that picks up the signal from a broadcast tower and then plays back the station's content.

In Spring, the event model works similarly:

  1. When Springa certain behavior occurs in the application (such as a user completing registration), the component that generated the behavior (such as the user service) will create an event and publish it.
  2. Once the event is published, Springit ApplicationContextwill act as a broadcaster and send the event to all registered listeners.
  3. After each listener receives the event, it will perform corresponding processing according to the type and content of the event (such as sending a welcome email, giving new user coupons, etc.).

  This is Springhow the event model works. It achieves decoupling between event sources, broadcasters and listeners, allowing event producers and consumers to develop and modify independently, enhancing the flexibility and maintainability of the program. sex.


2. Listener

2.1 Implement the ApplicationListener interface to create a listener

  First, we create a listener. In Springthe framework, the built-in listener interface is ApplicationListenera generic parameter that represents the specific event to be monitored. We can create custom listeners by implementing this interface.

  When everything Beanhas been initialized, Springan event will be published ContextRefreshedEventto inform all listeners that the application context is ready. We can create a listener to listen for ContextRefreshedEventthe event.

The entire code is as follows:

package com.example.demo.listener;

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

@Component
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    
    
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        System.out.println("MyContextRefreshedListener收到ContextRefreshedEvent事件!");
    }
}

Note that we @Componentmark this listener with an annotation so Springthat it can be found and automatically registered during packet scanning.

Next, we need to create a startup class to start IOCthe container and test this listener.

Main program:

package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    

    public static void main(String[] args) {
    
    
        System.out.println("开始初始化IOC容器...");
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.example.demo.listener");
        System.out.println("IOC容器初始化完成...");
        ctx.close();
        System.out.println("IOC容器已关闭...");
    }

}

Running this program will see the message printed in is output on the console MyContextRefreshedListener, indicating that the listener successfully received the event.

operation result:

Insert image description here

2.2 @EventListener annotation creates a listener

  In addition to ApplicationListenercreating listeners by implementing interfaces, we can also create them through annotations. SpringAnnotations are provided @EventListener, and we can use this annotation on any method to specify that this method should be called when certain events are received.

  For example, we can create a new listener to listen for ContextClosedEventthe event that the Springapplication context represented by this event is about to be closed:

Add a MyContextClosedListenerclass to facilitate comparison with the listener created by the previous interface.

@Component
public class MyContextClosedListener {
    
    
    @EventListener
    public void handleContextClosedEvent(ContextClosedEvent event) {
    
    
        System.out.println("MyContextClosedListener收到ContextClosedEvent事件!");
    }
}

operation result:

Insert image description here

  ContextClosedEventThe event is Springpublished when the application context is closed, which is usually Beanafter all singletons have been destroyed. By listening to this event, we can perform some cleanup work when the application context is closed.

2.3 Compare the creation methods of ApplicationListener interface and @EventListener annotation

  1. Use the ApplicationListener interface:

As mentioned above, since ApplicationListenerthe interface is a generic interface, this interface has a generic parameter that represents the specific event to be monitored.

Create ContextRefreshedEventa listener for the event:

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

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        System.out.println("Received ContextRefreshedEvent - Context refreshed!");
    }
}

Create ContextClosedEventa listener for the event:

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

@Component
public class ContextClosedListener implements ApplicationListener<ContextClosedEvent> {
    
    

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
    
    
        System.out.println("Received ContextClosedEvent - Context closed!");
    }
}
  1. Use @EventListener annotation:
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextEventListener {
    
    

    @EventListener
    public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
    
    
        System.out.println("Received ContextRefreshedEvent - Context refreshed!");
    }

    @EventListener
    public void handleContextClosedEvent(ContextClosedEvent event) {
    
    
        System.out.println("Received ContextClosedEvent - Context closed!");
    }
}

  In the above code, we use @EventListenerannotations to define two methods, processing ContextRefreshedEventand ContextClosedEventevents respectively. Whenever something ContextRefreshedEventisContextClosedEvent published, the corresponding listener will be triggered and the corresponding information will be printed on the console.

  • ContextRefreshedEventThe event Springis triggered when the container is initialized or refreshed. At this time, all Beanhave been fully loaded and post-processorhave been called, but the container has not yet started receiving requests.

  • ContextClosedEventThe event Springis triggered when the container is closed, before everything has been destroyed Bean. You can do some cleanup work after receiving this event.


3. Spring’s event mechanism

  In Spring, events ( Event) and listeners ( Listener) are two core concepts, which together constitute Springthe event mechanism of . This mechanism enables Springdecoupled interactions between components in applications by publishing and listening for events.

SpringThere is 4a default built-in event in

  • ApplicationEvent
  • ApplicationContextEvent
  • ContextRefreshedEventContextClosedEvent
  • ContextStartedEventContextStoppedEvent

Let's take a look at each

3.1 ApplicationEvent

  In Springis ApplicationEventthe abstract base class of all event models. It inherits from Javanative EventObjectand ApplicationEventis itself an abstract class. It does not define special methods or properties. It only contains the timestamp when the event occurs. This means that we can inherit ApplicationEventto create custom events.

  For example, if we want to create a custom event, we can directly inherit ApplicationEventthe class.

The complete code is as follows:

CreateCustomApplicationEvent

package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class CustomApplicationEvent extends ApplicationEvent {
    
    
    private String message;

    public CustomApplicationEvent(Object source, String message) {
    
    
        super(source);
        this.message = message;
    }

    public String getMessage() {
    
    
        return message;
    }
}

Then create a listener to listen for this custom event:

package com.example.demo.listener;

import com.example.demo.event.CustomApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomApplicationEventListener implements ApplicationListener<CustomApplicationEvent> {
    
    

    @Override
    public void onApplicationEvent(CustomApplicationEvent event) {
    
    
        System.out.println("Received custom event - " + event.getMessage());
    }
}

Finally, publish this custom event somewhere in your app:

package com.example.demo.event;

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

@Component
public class CustomEventPublisher {
    
    
    private final ApplicationEventPublisher publisher;

    public CustomEventPublisher(ApplicationEventPublisher publisher) {
    
    
        this.publisher = publisher;
    }

    public void doSomethingAndPublishAnEvent(final String message) {
    
    
        System.out.println("Publishing custom event.");
        CustomApplicationEvent customApplicationEvent = new CustomApplicationEvent(this, message);
        publisher.publishEvent(customApplicationEvent);
    }
}

  When the method is called doSomethingAndPublishAnEvent(), a custom message CustomApplicationEventListenerwill be received and printed out. This is a way to inherit custom events and listen for them.CustomApplicationEventApplicationEvent

Main program:

package com.example.demo;

import com.example.demo.event.CustomEventPublisher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
        CustomEventPublisher publisher = context.getBean(CustomEventPublisher.class);
        publisher.doSomethingAndPublishAnEvent("hello world");
    }
}

  Some people may wonder, context.getBean(CustomEventPublisher.class)why not report an error? CustomEventPublisherThere is only one constructor with parameters.

  From Spring 4.3the beginning, if the class has only one constructor, then Springthis constructor will automatically be regarded as the constructor we want to autowire, without explicit addition @Autowiredor @injectannotation. If a class has multiple constructors and no constructor is used @Autowiredor @injectannotated, Springthe parameterless constructor (if one exists) will be used to create an instance of the class. SpringIt will try to find the ones that have been created beanthat meet the constructor parameter requirements bean, and automatically beaninject these into the constructor method. This is called autowiring.

  In this example, CustomEventPublisherthe class has only one ApplicationEventPublisherconstructor with parameters. SpringWhen creating CustomEventPublisheran instance, it will try to find an already created ApplicationEventPublishertype beanthat satisfies the parameter requirements of this constructor.

ApplicationEventPublisherIt is   Springa built-in interface, and the corresponding instance has Springbeen Springautomatically created when the container starts, so Springyou can find a ApplicationEventPublishertype bean, and then beaninject this into CustomEventPublisherthe constructor, so that CustomEventPublisherthe instance can be successfully created.

  So, even if CustomEventPublisherthis class does not have a no-argument constructor, Springan instance of this class can be successfully created through the autowiring function.

operation result:

Insert image description here

3.2 ApplicationContextEvent

  ApplicationContextEventIs ApplicationEventa subclass of which represents events related to Springapplication context ( ). This abstract class receives an object as the event source ApplicationContextin the constructor ( ). This means that when an event is triggered, we can directly obtain the application context where the event occurred through the event object without performing additional operations.ApplicationContextsource

Insert image description here

  SpringThere are some built-in events, such as ContextRefreshedEventand ContextClosedEvent, which are ApplicationContextEventsubclasses of . ApplicationContextEventIs ApplicationEventa subclass of , specifically used to represent Springevents related to application context. Although ApplicationContextEventis an abstract class, in actual use, its concrete subclasses, such as ContextRefreshedEventand ContextClosedEvent, are usually used instead of directly ApplicationContextEvent. In addition, although we can create custom ApplicationContextEventsubclasses or ApplicationEventsubclasses to represent specific events, this is relatively rare because in most cases, the Springbuilt-in event classes can already meet our needs.

3.3 ContextRefreshedEvent 和 ContextClosedEvent

  ContextRefreshedEventThe event Springis triggered when the container is initialized or refreshed. At this time, all Beanhave been fully loaded and post-processorhave been called, but the container has not yet started receiving requests.

  ContextClosedEventThe event Springis triggered when the container is closed, before everything has been destroyed Bean. You can do some cleanup work after receiving this event.

  Here we once again demonstrate the implementation of the interface to create a listener, but 2.3unlike the section, we only create 1a class and handle ContextRefreshedEventthe and ContextClosedEventevents at the same time. The interface is implemented here ApplicationListener, and the generic parameters use the parent class of ContextRefreshedEventand .ContextClosedEventApplicationEvent

  Create a class in Springto listen to multiple events and then onApplicationEventcheck the type of event in the method.

The entire code is as follows:

package com.example.demo.listener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationContextEventListener implements ApplicationListener<ApplicationEvent> {
    
    

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    
    
        if (event instanceof ContextRefreshedEvent) {
    
    
            System.out.println("Context Refreshed Event received.");
            // Handle the event
        } else if (event instanceof ContextClosedEvent) {
    
    
            System.out.println("Context Closed Event received.");
            // Handle the event
        }
    }
}

Main program:

package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("开始初始化IOC容器...");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
        System.out.println("IOC容器初始化完成...");
        context.close();
        System.out.println("IOC容器已关闭...");
    }
}

operation result:

Insert image description here

  In this example, MyApplicationContextEventListeneronly the interface is implemented now ApplicationListener<ApplicationEvent>. Then in onApplicationEventthe method, we check the type of event and perform the appropriate action based on the type of event. This way we can handle multiple types of events in the same listener.

3.4 ContextStartedEvent 和 ContextStoppedEvent

  • ContextStartedEvent : This is Springthe startup event of the application context. This event is published when a method that implements Lifecyclethe interface is called . The publication of this event indicates that the application context has been started and everything has been initialized and ready to receive requests. We can listen to this event to perform some custom logic after the application context is started, such as starting a new thread, or connecting to a remote service, etc.BeanstartSpringSpringBean

  • ContextStoppedEvent : This is Springthe stopped event of the application context. This event is published when a method that implements Lifecyclethe interface is called . The publication of this event marks the beginning of the stopping process of the application context. At this time, it will stop receiving new requests and start destroying all . We can listen to this event to perform some cleanup logic before the application context stops, such as closing the database connection, releasing resources, etc.BeanstopSpringSpringSpringSingleton Bean

  Some people may wonder, what are the methods and methods that implement Lifecyclethe interface ? How is this different from and ?Beanstartstop@PostConstruct@PreDestroy

  LifecycleThe interface has startthis stopmethod2 , start()which will Beanbe called when the entire application context is started after all have been initialized, and stop()the method will be called when the application context is closed, but before all singletons Beanare destroyed. This means that these methods will affect the entire application context life cycle.

  In general, @PostConstructand @PreDestroymainly focus on Beanthe life cycle of a single, while Lifecyclethe interface focuses on the life cycle of the entire application context.

  Closer to home, back to the topic of this section, when Springthe container receives ContextStoppedEventthe event, it will stop all singletons Beanand release related resources.

The entire code is as follows:

package com.example.demo.listener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyContextStartStopEventListener implements ApplicationListener<ApplicationEvent> {
    
    

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    
    
        if (event instanceof ContextStartedEvent) {
    
    
            System.out.println("Context Started Event received.");
            // Handle the event
        } else if (event instanceof ContextStoppedEvent) {
    
    
            System.out.println("Context Stopped Event received.");
            // Handle the event
        }
    }
}
package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
        // 触发 ContextStartedEvent
        context.start();
        // 触发 ContextStoppedEvent
        context.stop();
    }
}

operation result:

Insert image description here

  For ContextStartedEventthe and ContextStoppedEventevents, you need to manually call context.start()and context.stop()to trigger these two events. ContextStartedEventEvents are posted when ApplicationContextstarted and all singletons are fully initialized, while events are posted when stopped.beanContextStoppedEventApplicationContext

  Why can events and events be triggered context.start()here ?context.stop()ContextStartedEventContextStoppedEvent

  Let's take a look at the source code. In Spring, AnnotationConfigApplicationContextthere is no direct implementation Lifecycleof the interface.

Insert image description here

  But from the picture we can see that AnnotationConfigApplicationContextinherited from GenericApplicationContext, GenericApplicationContextinherited from AbstractApplicationContextabstract class. AbstractApplicationContextThe class implements ConfigurableApplicationContextthe interface, and this ConfigurableApplicationContextinterface inherits Lifecyclethe interface.

  Therefore, from the perspective of class inheritance level, the interface AnnotationConfigApplicationContextis indirectly implemented Lifecycle. When we call the or method AnnotationConfigApplicationContexton the object , it will trigger the corresponding or event.start()stop()ContextStartedEventContextStoppedEvent

  In actual Springapplications, manual calls start()and stop()methods are rarely needed. This is because Springthe framework has already handled most of the life cycle control for us, such as beancreation and destruction, container initialization and shutdown, etc.


4. Custom event development

4.1 Comparison of triggering timing between annotation listeners and interface listeners

Requirement background: Assume that a forum application is being developed. When a new user successfully registers, the system needs to perform a series of operations. These operations include:

  1. Send a confirmation text message to the user;
  2. Send a confirmation email to the user's email address;
  3. Send a confirmation message to the user's on-site message center;

  To implement these operations, we can leverage Springthe event-driven model. Specifically, when the user registers successfully, we can publish a "User registration successful" event. This event will contain information about the newly registered user.

  Then, we can create multiple listeners to listen for this "user registration successful" event. These listeners correspond to the three operations mentioned above. When the listeners listen to the "user registration successful" event, they will perform their respective operations based on the user information in the event.

  For comparison here, the two methods of creating listeners will be used together. In actual development, only one method needs to be used.

The entire code is as follows:

First, let's define the event:

package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class UserRegistrationEvent extends ApplicationEvent {
    
    
    private String username;

    public UserRegistrationEvent(Object source, String username) {
    
    
        super(source);
        this.username = username;
    }

    public String getUsername() {
    
    
        return username;
    }
}

Next, let us define an interface listener to send SMS notifications:

package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class SmsNotificationListener implements ApplicationListener<UserRegistrationEvent> {
    
    
    @Override
    public void onApplicationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送短信通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}

We can also define an annotation listener to send email notifications:

package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class EmailNotificationListener {
    
    
    @EventListener
    public void handleUserRegistrationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送邮件通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}

And an annotation listener to send site message notifications:

package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class InternalMessageNotificationListener {
    
    
    @EventListener
    public void handleUserRegistrationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送站内信通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}

Finally, we need a method to publish events, which will be called after the user successfully registers. We can add this method to the registration service:

package com.example.demo.service;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class UserRegistrationService {
    
    
    @Autowired
    private ApplicationContext applicationContext;

    public void registerUser(String username) {
    
    
        // 用户注册逻辑......

        // 用户注册成功,发布事件
        UserRegistrationEvent event = new UserRegistrationEvent(this, username);
        applicationContext.publishEvent(event);
    }
}

The above is an example of using two different listeners. If you run this code, you will find that the annotation listener (email notification and site message notification) is triggered before the interface listener (SMS notification).

If not used SpringBoot, we can use Springthe traditional application context to initialize and start the application

The main program is as follows:

package com.example.demo;

import com.example.demo.service.UserRegistrationService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
        UserRegistrationService userRegistrationService = context.getBean(UserRegistrationService.class);
        userRegistrationService.registerUser("testUser");
        context.close();
    }
}

UserRegistrationServiceObtained from the application context Bean, the calling registerUsermethod triggers the event.

operation result:

Insert image description here

From here we can also draw a conclusion: annotation listeners are triggered earlier than interface listeners .

If using SpringBootthe main program of the framework:

package com.example.demo;

import com.example.demo.service.UserRegistrationService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;


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

    @Bean
    public CommandLineRunner commandLineRunner(UserRegistrationService userRegistrationService) {
    
    
        return args -> {
    
    
            userRegistrationService.registerUser("testUser");
        };
    }
}

operation result:

Insert image description here

  In this example, we create an SpringBootapplication. By injecting UserRegistrationServiceand calling its registerUsermethods, we can trigger the user registration event. In an actual application, this service may be called in a controller or other services.

  In the order of the life cycle in the previous article, we mentioned Beanattribute assignment, @PostConstructannotations, methods InitializingBeanafter implementing the interface afterPropertiesSetand init-methodspecified methods during initialization. So what is the relationship between the order of annotation listeners here and the order of these life cycles?

For listeners:

  • Listeners annotated with @EventListenerApplicationContext : This type of listener will be triggered when the application is refreshed. This "refresh" means that all Beandefinitions have been loaded, automatic assembly is completed, that is, the constructors and settermethods have been executed. But at this time, the initialization callback (such as @PostConstructthe method or definition of InitializingBeanthe interface ) has not yet started execution.afterPropertiesSetinit-method

We can simplify this to: @EventListenerthe annotated listener Beanstarts working when everything has been loaded and autowired, but the initialization callback has not yet been executed.

  • Listeners that implement the ApplicationListener interface : This type of listener first acts as a listener Beanand will go through all the normal Beanlife cycle stages. This includes constructor calls, settermethod calls, and initialization callbacks. These listeners will be fired after the initialization of all singletons Beanis completed, that is , after the event is posted.ContextRefreshedEvent

We can simplify this to: ApplicationListenerthe listener that implements the interface starts working after it has gone through the regular Beanlife cycle and all singletons have been initialized.Bean

  The main difference between these two types of listeners is when they start working: @EventListenerlisteners using annotations start working before the initialization callback, while ApplicationListenerlisteners implementing interfaces Beanstart working after all singleton initialization is completed.

4.2 @Order annotation adjusts the triggering order of listeners

  In the example just now, because the monitoring of sending text messages is interface-based, and the annotation-based listener is triggered earlier than the interface-based listener, it is not triggered until after the meeting. Here we use @Orderannotations to forcefully change the triggering order.

  @OrderAnnotations can be used on classes or methods. They accept an integer value as a parameter. This parameter represents the "priority" of the annotated class or method. The smaller the value, the higher the priority and the earlier it is called. @OrderThe value of can be negative and intwithin the range.

  Suppose we want SMS notifications to have the highest priority, followed by in-site email notifications, and finally email notifications. Then we can use @Orderannotations as follows

So, let’s modify the order of the listeners

package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(1)
public class SmsNotificationListener implements ApplicationListener<UserRegistrationEvent> {
    
    
    @Override
    public void onApplicationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送短信通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}
package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class InternalMessageNotificationListener {
    
    
    @EventListener
    @Order(2)
    public void handleUserRegistrationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送站内信通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}
package com.example.demo.listener;

import com.example.demo.event.UserRegistrationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class EmailNotificationListener {
    
    
    @EventListener
    @Order(3)
    public void handleUserRegistrationEvent(UserRegistrationEvent event) {
    
    
        System.out.println("发送邮件通知,恭喜用户 " + event.getUsername() + " 注册成功!");
    }
}

  It may be due to version reasons. After my testing, if the listener is created by annotation, @Orderwriting it on the class will invalidate the sequence control of all listeners. Therefore, if you want to add an interface listener, @Orderplace it on the class, and if you want to add an annotation listener, @Orderplace it on the method.

operation result:

Insert image description here

  1. For ApplicationListenerlisteners that implement interfaces (ie, interface listeners), if not specified @Order, their execution order is usually @Orderafter all specified listeners. This is because Springunspecified @Orderlisteners are given LOWEST_PRECEDENCEpriority by default.

  2. For @EventListenermethods that use annotations (i.e., annotated listeners), if they are not explicitly specified @Order, their execution order is specified by default @Order(0).

  NOTE: We should rely less on event processing order to better decouple our code. Although @Orderyou can specify the order in which listeners are executed, it does not change the asynchronous nature of event publishing. @OrderAnnotations can only guarantee the calling order of the listener. The calling of the event listener may be executed concurrently in multiple threads, so the order cannot be guaranteed, and it is not applicable in distributed applications. The order cannot be guaranteed in multiple application contexts.



Welcome to the one-click triple connection~

If you have any questions, please leave a message, let's discuss and learn together

----------------------Talk is cheap, show me the code----- ------------------

Guess you like

Origin blog.csdn.net/qq_34115899/article/details/131259811