SpringBoot_Events
2025-01-22 08:19:30    1.4k 字   
This post is also available in English and alternative languages.

Spring Framework版本:5.1.6.RELEASE (点击跳转官方文档)
SpringBoot版本:2.1.4.RELEASE (点击跳转官方文档)


Event handling in the ApplicationContext is provided through the ApplicationEvent class and the ApplicationListener interface. If a bean that implements the ApplicationListener interface is deployed into the context, every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified. Essentially, this is the standard Observer design pattern.

ApplicationContext 通过 ApplicationEvent 类和 ApplicationListener 接口进行事件处理。 如果将实现 ApplicationListener 接口的 Bean 注入到上下文中,则每次使用 ApplicationContext 发布 ApplicationEvent 时,都会通知该 Bean。 本质上,这是标准的观察者设计模式,是事件驱动模型在设计层面的体现。


1. 概览

以下是 SpringBoot 启动过程中相关的事件类型(按序):

事件所属说明
ApplicationStartingEventSpring Boot应用程序启动,ApplicationContext(容器) 创建之前触发的事件
ApplicationEnvironmentPreparedEventSpring Boot容器环境准备已完成,容器创建之前触发的事件
ApplicationContextInitializedEventSpring Boot容器已创建,容器中所有 ApplicationContextInitializer 实现类的 initialize 方法调用结束后触发的事件
ApplicationPreparedEventSpring Boot容器已创建,还没有被刷新,但已准备完毕。
ContextRefreshedEventSpring Framework容器已经刷新并准备好使用。
ServletWebServerInitializedEventSpring Boot
ApplicationStartedEventSpring Boot在容器刷新后,并且还没有调用任何程序或命令行程序时发布的事件。
ApplicationReadyEventSpring Boot在容器/服务已经准备好提供请求时发布的事件。
ApplicationFailedEventSpring Boot在容器启动出错时发布的事件。

值得注意的是,有些事件是在 ApplicationContext 创建之前触发的,因此不能使用注解进行声明。

Some events are actually triggered before the ApplicationContext is created, so you cannot register a listener on those as a @Bean. You can register them with the SpringApplication.addListeners(…) method or the SpringApplicationBuilder.listeners(…) method.

If you want those listeners to be registered automatically, regardless of the way the application is created, you can add a META-INF/spring.factories file to your project and reference your listener(s) by using the org.springframework.context.ApplicationListener key, as shown in the following example:

org.springframework.context.ApplicationListener=com.example.project.MyListener


以下是部分事件被执行的时机及各自的顺序:

springboot 事件流程图(来源:https://developer.aliyun.com/article/767202)

2. ApplicationStartingEvent

ApplicationStartingEvent 在容器刚启动,ApplicationContext 创建之前触发的事件。不能使用注解进行声明。

1
2
3
4
5
6
public class ApplicationStartingEventListenerExample implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println(">>>>> ApplicationStartingEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#run(java.lang.String...)


3. ApplicationEnvironmentPreparedEvent

ApplicationEnvironmentPreparedEvent 在容器启动后,并且环境信息可用于检查和修改时发布的事件。不能使用注解进行声明。

1
2
3
4
5
6
public class ApplicationEnvironmentPreparedEventListenerExample implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println(">>>>> ApplicationEnvironmentPreparedEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#prepareEnvironment


4. ApplicationContextInitializedEvent

ApplicationContextInitializedEvent 在容器初始化时发布的事件。不能使用注解进行声明。

1
2
3
4
5
6
public class ApplicationContextInitializedEventListenerExample implements ApplicationListener<ApplicationContextInitializedEvent> {
@Override
public void onApplicationEvent(ApplicationContextInitializedEvent event) {
System.out.println(">>>>> ApplicationContextInitializedEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#prepareContext


5. ApplicationPreparedEvent

ApplicationPreparedEvent 在容器准备就绪时发布的事件。可以使用注解进行声明。

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationPreparedEventListenerExample implements ApplicationListener<ApplicationPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
log.info(">>>>> ApplicationPreparedEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#prepareContext

使用 @EventListener 注解方式:

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationPreparedEventListenerExample2 {
@EventListener(value = ApplicationPreparedEvent.class)
public void onApplicationEvent(ApplicationPreparedEvent event) {
log.info("》》》》》 ApplicationPreparedEvent, do something...");
}
}

6. ApplicationStartedEvent

ApplicationStartedEvent 在容器刷新后,并且还没有调用任何程序或命令行程序时发布的事件。可以使用注解进行声明。

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationStartedEventLinstenerExample implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
log.info(">>>>> ApplicationStartedEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#run(java.lang.String...)

使用 @EventListener 注解方式

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationStartedEventLinstenerExample2 {
@EventListener(value = ApplicationStartedEvent.class)
public void onApplicationEvent(ApplicationStartedEvent event) {
log.info("》》》》》 ApplicationStartedEvent, do something...");
}
}

7. ApplicationReadyEvent

ApplicationReadyEvent 在 容器/服务 已经准备好提供请求时发布的事件,可以使用注解进行声明。

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationReadyEventListenerExample implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info(">>>>> ApplicationReadyEvent, do something...");
}
}

源码中被调用的位置:org.springframework.boot.SpringApplication#run(java.lang.String...)

使用 @EventListener 注解方式

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class ApplicationReadyEventListenerExample2 {
@EventListener(value = ApplicationReadyEvent.class)
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("》》》》》 ApplicationReadyEvent, do something...");
}
}

8. ApplicationFailedEvent

忽略


9. 使用 spel 表达式

@EventListener 中的 condition 属性可以指定 SpEL 表达式条件,只有当事件内容是 update 时才监听器方法才处理该事件。

1
2
3
4
5
6
7
8
@Slf4j
@Component
public class CustomizeEventListener {
@EventListener(value = CustomizeEvent.class, condition = "#event.code== 'update'")
public void onApplicationEvent(CustomizeEvent event) {
log.info(">>>>> CustomizeEvent 只处理 update");
}
}

10. 示例代码

启动类示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Slf4j
@SpringBootApplication
public class ApplicationSpringBootExample05 {

@Resource
ApplicationEventPublisher applicationEventPublisher;

public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication();
springApplication.setSources(new HashSet<>(Collections.singletonList(ApplicationSpringBootExample05.class.getName())));
springApplication.addListeners(new ApplicationStartingEventListenerExample());
springApplication.addListeners(new ApplicationEnvironmentPreparedEventListenerExample());
springApplication.addListeners(new ApplicationContextInitializedEventListenerExample());

ConfigurableApplicationContext applicationContext = springApplication.run(args);
ApplicationSpringBootExample05 springBootExample05 = applicationContext.getBean(ApplicationSpringBootExample05.class);
springBootExample05.sendCustomizeEvent();

log.info("启动完成...");
}

private void sendCustomizeEvent() {
log.info("发送 CustomizeEvent save");
applicationEventPublisher.publishEvent(new CustomizeEvent(this, "save", "123"));

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException ignored) {
}

log.info("发送 CustomizeEvent update");
applicationEventPublisher.publishEvent(new CustomizeEvent(this, "update", "123"));
}
}

public class CustomizeEvent extends ApplicationEvent {
private String code;
private String message;

public CustomizeEvent(Object source, String code, String message) {
super(source);
this.code = code;
this.message = message;
}
// get/set
}

11. Reference