Article directory
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:
-
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. -
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.
-
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.
-
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:
- When
Spring
a 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. - Once the event is published,
Spring
itApplicationContext
will act as a broadcaster and send the event to all registered listeners. - 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 Spring
how 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 Spring
the framework, the built-in listener interface is ApplicationListener
a generic parameter that represents the specific event to be monitored. We can create custom listeners by implementing this interface.
When everything Bean
has been initialized, Spring
an event will be published ContextRefreshedEvent
to inform all listeners that the application context is ready. We can create a listener to listen for ContextRefreshedEvent
the 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 @Component
mark this listener with an annotation so Spring
that it can be found and automatically registered during packet scanning.
Next, we need to create a startup class to start IOC
the 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:
2.2 @EventListener annotation creates a listener
In addition to ApplicationListener
creating listeners by implementing interfaces, we can also create them through annotations. Spring
Annotations 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 ContextClosedEvent
the event that the Spring
application context represented by this event is about to be closed:
Add a MyContextClosedListener
class 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:
ContextClosedEvent
The event is Spring
published when the application context is closed, which is usually Bean
after 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
- Use the ApplicationListener interface:
As mentioned above, since ApplicationListener
the interface is a generic interface, this interface has a generic parameter that represents the specific event to be monitored.
Create ContextRefreshedEvent
a 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 ContextClosedEvent
a 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!");
}
}
- 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 @EventListener
annotations to define two methods, processing ContextRefreshedEvent
and ContextClosedEvent
events respectively. Whenever something ContextRefreshedEvent
isContextClosedEvent
published, the corresponding listener will be triggered and the corresponding information will be printed on the console.
-
ContextRefreshedEvent
The eventSpring
is triggered when the container is initialized or refreshed. At this time, allBean
have been fully loaded andpost-processor
have been called, but the container has not yet started receiving requests. -
ContextClosedEvent
The eventSpring
is triggered when the container is closed, before everything has been destroyedBean
. 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 Spring
the event mechanism of . This mechanism enables Spring
decoupled interactions between components in applications by publishing and listening for events.
Spring
There is 4
a default built-in event in
- ApplicationEvent
- ApplicationContextEvent
- ContextRefreshedEvent 和 ContextClosedEvent
- ContextStartedEvent 和 ContextStoppedEvent
Let's take a look at each
3.1 ApplicationEvent
In Spring
is ApplicationEvent
the abstract base class of all event models. It inherits from Java
native EventObject
and ApplicationEvent
is 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 ApplicationEvent
to create custom events.
For example, if we want to create a custom event, we can directly inherit ApplicationEvent
the 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 CustomApplicationEventListener
will be received and printed out. This is a way to inherit custom events and listen for them.CustomApplicationEvent
ApplicationEvent
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? CustomEventPublisher
There is only one constructor with parameters.
From Spring 4.3
the beginning, if the class has only one constructor, then Spring
this constructor will automatically be regarded as the constructor we want to autowire, without explicit addition @Autowired
or @inject
annotation. If a class has multiple constructors and no constructor is used @Autowired
or @inject
annotated, Spring
the parameterless constructor (if one exists) will be used to create an instance of the class. Spring
It will try to find the ones that have been created bean
that meet the constructor parameter requirements bean
, and automatically bean
inject these into the constructor method. This is called autowiring.
In this example, CustomEventPublisher
the class has only one ApplicationEventPublisher
constructor with parameters. Spring
When creating CustomEventPublisher
an instance, it will try to find an already created ApplicationEventPublisher
type bean
that satisfies the parameter requirements of this constructor.
ApplicationEventPublisher
It is Spring
a built-in interface, and the corresponding instance has Spring
been Spring
automatically created when the container starts, so Spring
you can find a ApplicationEventPublisher
type bean
, and then bean
inject this into CustomEventPublisher
the constructor, so that CustomEventPublisher
the instance can be successfully created.
So, even if CustomEventPublisher
this class does not have a no-argument constructor, Spring
an instance of this class can be successfully created through the autowiring function.
operation result:
3.2 ApplicationContextEvent
ApplicationContextEvent
Is ApplicationEvent
a subclass of which represents events related to Spring
application context ( ). This abstract class receives an object as the event source ApplicationContext
in 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.ApplicationContext
source
Spring
There are some built-in events, such as ContextRefreshedEvent
and ContextClosedEvent
, which are ApplicationContextEvent
subclasses of . ApplicationContextEvent
Is ApplicationEvent
a subclass of , specifically used to represent Spring
events related to application context. Although ApplicationContextEvent
is an abstract class, in actual use, its concrete subclasses, such as ContextRefreshedEvent
and ContextClosedEvent
, are usually used instead of directly ApplicationContextEvent
. In addition, although we can create custom ApplicationContextEvent
subclasses or ApplicationEvent
subclasses to represent specific events, this is relatively rare because in most cases, the Spring
built-in event classes can already meet our needs.
3.3 ContextRefreshedEvent 和 ContextClosedEvent
ContextRefreshedEvent
The event Spring
is triggered when the container is initialized or refreshed. At this time, all Bean
have been fully loaded and post-processor
have been called, but the container has not yet started receiving requests.
ContextClosedEvent
The event Spring
is 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.3
unlike the section, we only create 1
a class and handle ContextRefreshedEvent
the and ContextClosedEvent
events at the same time. The interface is implemented here ApplicationListener
, and the generic parameters use the parent class of ContextRefreshedEvent
and .ContextClosedEvent
ApplicationEvent
Create a class in Spring
to listen to multiple events and then onApplicationEvent
check 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:
In this example, MyApplicationContextEventListener
only the interface is implemented now ApplicationListener<ApplicationEvent>
. Then in onApplicationEvent
the 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
Spring
the startup event of the application context. This event is published when a method that implementsLifecycle
the 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.Bean
start
Spring
Spring
Bean
-
ContextStoppedEvent : This is
Spring
the stopped event of the application context. This event is published when a method that implementsLifecycle
the 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.Bean
stop
Spring
Spring
Spring
Singleton Bean
Some people may wonder, what are the methods and methods that implement Lifecycle
the interface ? How is this different from and ?Bean
start
stop
@PostConstruct
@PreDestroy
Lifecycle
The interface has start
this stop
method2
, start()
which will Bean
be 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 Bean
are destroyed. This means that these methods will affect the entire application context life cycle.
In general, @PostConstruct
and @PreDestroy
mainly focus on Bean
the life cycle of a single, while Lifecycle
the interface focuses on the life cycle of the entire application context.
Closer to home, back to the topic of this section, when Spring
the container receives ContextStoppedEvent
the event, it will stop all singletons Bean
and 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:
For ContextStartedEvent
the and ContextStoppedEvent
events, you need to manually call context.start()
and context.stop()
to trigger these two events. ContextStartedEvent
Events are posted when ApplicationContext
started and all singletons are fully initialized, while events are posted when stopped.bean
ContextStoppedEvent
ApplicationContext
Why can events and events be triggered context.start()
here ?context.stop()
ContextStartedEvent
ContextStoppedEvent
Let's take a look at the source code. In Spring
, AnnotationConfigApplicationContext
there is no direct implementation Lifecycle
of the interface.
But from the picture we can see that AnnotationConfigApplicationContext
inherited from GenericApplicationContext
, GenericApplicationContext
inherited from AbstractApplicationContext
abstract class. AbstractApplicationContext
The class implements ConfigurableApplicationContext
the interface, and this ConfigurableApplicationContext
interface inherits Lifecycle
the interface.
Therefore, from the perspective of class inheritance level, the interface AnnotationConfigApplicationContext
is indirectly implemented Lifecycle
. When we call the or method AnnotationConfigApplicationContext
on the object , it will trigger the corresponding or event.start()
stop()
ContextStartedEvent
ContextStoppedEvent
In actual Spring
applications, manual calls start()
and stop()
methods are rarely needed. This is because Spring
the framework has already handled most of the life cycle control for us, such as bean
creation 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:
- Send a confirmation text message to the user;
- Send a confirmation email to the user's email address;
- Send a confirmation message to the user's on-site message center;
To implement these operations, we can leverage Spring
the 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 Spring
the 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();
}
}
UserRegistrationService
Obtained from the application context Bean
, the calling registerUser
method triggers the event.
operation result:
From here we can also draw a conclusion: annotation listeners are triggered earlier than interface listeners .
If using SpringBoot
the 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:
In this example, we create an SpringBoot
application. By injecting UserRegistrationService
and calling its registerUser
methods, 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 Bean
attribute assignment, @PostConstruct
annotations, methods InitializingBean
after implementing the interface afterPropertiesSet
and init-method
specified 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 @EventListener
ApplicationContext
: This type of listener will be triggered when the application is refreshed. This "refresh" means that allBean
definitions have been loaded, automatic assembly is completed, that is, the constructors andsetter
methods have been executed. But at this time, the initialization callback (such as@PostConstruct
the method or definition ofInitializingBean
the interface ) has not yet started execution.afterPropertiesSet
init-method
We can simplify this to: @EventListener
the annotated listener Bean
starts 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
Bean
and will go through all the normalBean
life cycle stages. This includes constructor calls,setter
method calls, and initialization callbacks. These listeners will be fired after the initialization of all singletonsBean
is completed, that is , after the event is posted.ContextRefreshedEvent
We can simplify this to: ApplicationListener
the listener that implements the interface starts working after it has gone through the regular Bean
life cycle and all singletons have been initialized.Bean
The main difference between these two types of listeners is when they start working: @EventListener
listeners using annotations start working before the initialization callback, while ApplicationListener
listeners implementing interfaces Bean
start 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 @Order
annotations to forcefully change the triggering order.
@Order
Annotations 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. @Order
The value of can be negative and int
within 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 @Order
annotations 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, @Order
writing it on the class will invalidate the sequence control of all listeners. Therefore, if you want to add an interface listener, @Order
place it on the class, and if you want to add an annotation listener, @Order
place it on the method.
operation result:
-
For
ApplicationListener
listeners that implement interfaces (ie, interface listeners), if not specified@Order
, their execution order is usually@Order
after all specified listeners. This is becauseSpring
unspecified@Order
listeners are givenLOWEST_PRECEDENCE
priority by default. -
For
@EventListener
methods 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 @Order
you can specify the order in which listeners are executed, it does not change the asynchronous nature of event publishing. @Order
Annotations 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----- ------------------