SpringBoot-事件监听机制
SpringBoot-事件监听机制
概要
在 Spring Boot 中,事件(Event)是一种在应用程序中用于通知和响应状态或动作变化的机制。
通过事件机制,不同的组件可以松散耦合地协作,实现模块化和可扩展的应用程序架构。事件的作用在于提供了一种解耦的方式,使得应用程序的不同部分能够相互通信和交互,而不需要直接引用彼此的实现细节。
监听器(Listener)用于监听应用程序中发生的事件,以便在事件发生时执行特定的逻辑。
说明:本文参考的SpringBoot版本是2.6.13
一、Spring Boot 启动事件顺序
事件执行顺序:
1. ApplicationStartingEvent
SpringApplication正在启动时但还未做任何处理之前时触发。
典型用途:初始化非常早期的资源,记录启动日志等
2. ApplicationEnvironmentPreparedEvent
spring boot 环境已经准备好,但上下文还没有创建时触发。
典型用途:可以在这个阶段调整环境配置,例如添加或修改属性源
3. ApplicationContextInitializedEvent
spring boot 上下文准备好但尚未刷新,bean的定义被加载之前时触发。
典型用途:执行在上下文刷新之前所需的操作
4. ApplicationPreparedEvent
spring boot 上下文加载后但尚未刷新,所有的 bean 定义已加载,但还没有任何 bean 被创建时触发。
典型用途:在所有 bean 定义加载后但还未实例化之前执行一些操作。
5. ApplicationStartedEvent
spring boot 上下文已刷新,此时应用已启动但还未准备好接受请求时触发。
典型用途:执行需要在应用程序刚刚启动后运行的任务
6. ApplicationReadyEvent
spring boot 上下文已刷新,应用程序已启动且准备好接受请求时触发。
典型用途:执行任何需要在应用程序已完全启动后运行的操作,例如启动后检查或通知
7. ApplicationFailedEvent
spring boot 启动过程中发生异常时触发。
典型用途:在启动失败时执行清理操作,记录错误日志等
二、自定义启动事件监听器
既然我们知道了 Spring Boot 在启动过程中的各个事件,那么我们就可以在每个环节来处理一些我们想做的事情,只需要自定义一个监听器来监听某个事件就可以了。
1. META-INF/spring.factories 文件
目录:
配置内容:org.springframework.boot.SpringApplicationRunListener=com.yp.listeners.MyApplicationRunListener
2. 自定义创建执行监听器的类
1 public class MyApplicationRunListener implements SpringApplicationRunListener { 2 3 /** 4 * !!! 构造方法必须这么写 5 * @param application 6 * @param args 7 */ 8 public MyApplicationRunListener(SpringApplication application, String[] args) { 9 } 10 11 @Override 12 public void starting(ConfigurableBootstrapContext bootstrapContext) { 13 System.out.println("Application 启动"); 14 } 15 16 @Override 17 public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { 18 System.out.println("环境已准备完毕"); 19 } 20 21 @Override 22 public void contextPrepared(ConfigurableApplicationContext context) { 23 System.out.println("在创建和准备ApplicationContext之后,但在加载源之前调用"); 24 } 25 26 @Override 27 public void contextLoaded(ConfigurableApplicationContext context) { 28 System.out.println("上下文准备完毕,未刷新"); 29 } 30 31 @Override 32 public void started(ConfigurableApplicationContext context, Duration timeTaken) { 33 System.out.println("上下文已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners"); 34 } 35 36 @Override 37 public void ready(ConfigurableApplicationContext context, Duration timeTaken) { 38 System.out.println("在刷新应用程序上下文并且调用了所有CommandLineRunner和ApplicationRunner之后,在运行方法完成之前立即调用"); 39 } 40 41 @Override 42 public void failed(ConfigurableApplicationContext context, Throwable exception) { 43 System.out.println("当运行应用程序时发生故障时调用"); 44 } 45 }
3. SpringApplicationRunListeners 的创建
创建的入口是在 SpringApplication 的 run方法中,通过 getRunListeners 方法来创建的。
1 // 生成事件发布对象 2 // 创建 SpringApplicationRunListeners 并把 默认的事件监听器赋值给 该对象 3 // 从 spring.factories文件中加载 SpringApplicationRunListener 的接口实现类 4 // 具体加载的是 EventPublishingRunListener 5 SpringApplicationRunListeners listeners = getRunListeners(args);
SpringApplication#getRunListeners 方法:
1 private SpringApplicationRunListeners getRunListeners(String[] args) { 2 Class<?>[] types = new Class[]{SpringApplication.class, String[].class}; 3 4 // 也是通过该方法 getSpringFactoriesInstances 指定 SpringApplicationRunListener 类型来加载的
// 注意下参数 this 就是当前的 SpringApplication 对象,用于获取监听器集合 7 return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); 8 }
可以看到参数 this,args。这就是为什么我们边上边自定义执行监听器的构造方法要固定参数的原因,因为根据这两个参数找构造方法来实例化执行监听器类的。
三、执行监听器的执行时机
执行时机有 7 种情况: starting、environmentPrepared、contextPrepared、contextLoaded、started、ready、failed
结合SpringBoot的启动过程,再来看看执行时机:
四、EventPublishingRunListener
SpringApplicationRunListener的实现类只有EventPublishingRunListener,实现SpringApplicationRunListener接口的方法都是包装一个Spring事件并进行广播,例如:
如下图:
EventPublishingRunListener,部分源码如下:
1 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { 2 private final SpringApplication application; 3 private final String[] args; 4 private final SimpleApplicationEventMulticaster initialMulticaster; 5 6 //构造函数 7 public EventPublishingRunListener(SpringApplication application, String[] args) { 8 this.application = application; 9 this.args = args; 10 11 // 创建多播器 12 this.initialMulticaster = new SimpleApplicationEventMulticaster(); 13 Iterator var3 = application.getListeners().iterator(); 14 15 // 将应用中的所有 ApplicationListener 注册到该多播器中 16 while(var3.hasNext()) { 17 ApplicationListener<?> listener = (ApplicationListener)var3.next(); 18 this.initialMulticaster.addApplicationListener(listener); 19 } 20 21 } 22 23 //... 24 }
说明:这段代码的主要作用是在 Spring Boot 启动过程中创建一个 SimpleApplicationEventMulticaster(事件多播器),并将应用中的所有 ApplicationListener 注册到该多播器中。这样,后续在应用启动的过程中,可以通过这个多播器发布事件,通知所有注册的监听器,从而实现事件驱动的应用启动流程。
下面对其中涉及的事件的广播与发布进行详细分析:
通过这种事件机制,Spring Boot 能够在启动过程的不同阶段通知监听器,并确保事件在应用程序的整个生命周期内能够被处理。
1. 事件广播(Broadcasting)
在启动过程中,EventPublishingRunListener 使用成员属性 initialMulticaster 来广播事件。
在启动过程的早期阶段(starting、environmentPrepared、contextPrepared、contextLoaded),SpringBoot 还未完全初始化上下文,因此使用 multicastEvent 方法来广播事件。
这种广播机制会将事件传递到所有注册的监听器,而不是像普通事件那样通过 ApplicationContext 进行发布。
在启动过程中,事件的广播是为了确保所有相关的监听器能够接收到启动期间发生的事件。
2. 事件发布(Publishing)
事件发布通常是通过 ApplicationContext 的 publishEvent 方法进行的。它通常发生在应用上下文已经完全初始化后,比如started、ready、failed。通过publishEvent 方法将事件传递给上下文,同时使用其内部的 ApplicationEventMulticaster 多播器来广播事件。
3. 事件处理
ApplicationEventMulticaster 遍历所有的监听器,并调用它们的处理方法,将事件传递给每个监听器。
五、SpringApplication 与 ApplicationContext 中的事件监听
监听器会存在于两个地方,一个是 SpringApplication,一个是 ApplicationContext。
程序刚启动的时候,比如执行到 starting 阶段的时候,我们要扩展一些功能,肯定不能在 ApplicationContext 上添加监听器,因为这个阶段,还没有ApplicationContext 这个对象。注册到 SpringApplication 上是最自然的选择。
在 Spring Boot 中,SpringApplicationRunListener 和 ApplicationListener 都用于监听和处理事件,但它们的触发机制有所不同。SpringApplicationRunListener 的事件由 SpringApplication 触发,而 ApplicationListener 的事件由 ApplicationContext 触发。理解这两者的机制及其转换关系可以帮助更好地管理应用的事件。
1. 触发机制的区别
1)SpringApplicationRunListener
SpringApplicationRunListener 是用于监听应用程序启动过程的监听器,而不是直接的事件监听器。它其实是用来在整个启动流程中接收不同执行点事件通知的监听者,SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。
触发:通过 SpringApplication 在启动过程中的不同阶段(如 starting、environmentPrepared、contextPrepared 等)触发。
事件类型:通常与 Spring Boot 启动过程相关的事件,如 ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent 等。
2)ApplicationListener
触发:由 ApplicationContext 中的事件广播器(ApplicationEventMulticaster)触发。
事件类型:与 Spring 应用的运行时状态相关的事件,如 ContextRefreshedEvent、ContextClosedEvent 等。
2. 转换机制
虽然 SpringApplicationRunListener 和 ApplicationListener 的事件触发机制不同,但 Spring Boot 默认使用的 SpringApplicationRunListener 实现类 EventPublishingRunListener 可以桥接这两者。
EventPublishingRunListener 的作用是将 SpringApplicationRunListener 的事件转发到 ApplicationContext 的事件广播器中。这使得在启动过程中生成的事件可以被 Spring 上下文中的 ApplicationListener监听到。
3. 源码分析
以下是如何通过 EventPublishingRunListener 实现这两者的转换
EventPublishingRunListener 实现了 SpringApplicationRunListener 接口。它会在 Spring Boot 启动的不同阶段创建和广播事件。
1 public class EventPublishingRunListener implements SpringApplicationRunListener { 2 private final ApplicationEventMulticaster initialMulticaster; 3 4 public EventPublishingRunListener(SpringApplication application, String[] args) { 5 // Constructor implementation 6 this.initialMulticaster = new SimpleApplicationEventMulticaster(); 7 } 8 9 @Override 10 public void starting(ConfigurableBootstrapContext bootstrapContext) { 11 this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, application, args)); 12 } 13 14 @Override 15 public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { 16 this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, application, args, environment)); 17 } 18 19 @Override 20 public void contextPrepared(ConfigurableApplicationContext context) { 21 this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(application, args, context)); 22 } 23 }
initialMulticaster 是 EventPublishingRunListener 的关键成员,它是 ApplicationEventMulticaster 的一个实现(通常是 SimpleApplicationEventMulticaster)。multicastEvent 方法会将事件广播到所有注册的监听器中。
注册和监听:EventPublishingRunListener 通过 initialMulticaster 将事件广播给 ApplicationContext 中的所有监听器。这些事件可以由上下文中的 ApplicationListener 处理。
4. 总结
SpringApplicationRunListener:主要用于处理 Spring Boot 启动过程中的事件。
ApplicationListener:主要用于处理 Spring 应用运行时的事件。
EventPublishingRunListener 是一个桥接器,负责将启动过程中的事件转发到 Spring 上下文中,允许 ApplicationListener 接收到并处理这些事件。这种机制使得 Spring Boot 启动过程中的事件可以被上下文中的监听器所处理,实现了两者之间的转换。这种设计使得启动过程的事件可以与运行时事件统一处理,增强了灵活性和可扩展性。
参考链接:https://www.cnblogs.com/kukuxjx/p/17373029.html