Springboot事件监听机制——事件发布(二)

Spring中的事件监听
前面我们大致了解了一下事件监听,我们接着来看看Spring中的事件监听是如何使用的。这里我选用了springboot 1.5.7的源码来学习。

在启动类中执行SpringApplication.run()后,首先会执行SpringApplication类的initialize(Object[] sources)方法,然后初始化initializers和listeners两个集合,initializers集合存放的是springboot从配置文件自动装配的6个ApplicationContextInitializer对应的类,listeners集合存放的是从配置文件自动装配的10个ApplicationListener对应的类。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个加载方式是通过读取配置文件中类的路径,然后利用反射生成的对象。具体方法如下:
在这里插入图片描述

接下来,我们看run(String… args)方法中的执行内容:
在这里插入图片描述
首先会通过getRunListeners(String[] args)方法去从配置文件读取key=SpringApplicationRunListener对应的类名然后通过反射生成对象,这里就一个EventPublishingRunListener对象。

接着执行listeners.starting();方法。这里会调用EventPublishingRunListener的starting()方法,查看源码,我们看到这里是调用一个广播器发送广播的方法
在这里插入图片描述
继续跟踪,执行到了SimpleApplicationEventMulticaster类中,通过名字我们可以知道这是一个广播器,用于专门发送事件的。
在这里插入图片描述
最终在这里调用发送事件的方法。这里的eventType是ApplicationStartedEvent。通过getApplicationListeners(event, type)可以得到4个listener:
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
LiquibaseServiceLocatorApplicationListener

注意:这里有个判断,如果有Executor不为空,就说明是开启线程异步发送的,否则就是同步发送。
我们如何开启异步发送,这个我们后面说。

我们接着看run()方法中的prepareEnvironment(listeners,applicationArguments);方法:
在这里插入图片描述
看到这里有个listeners.environmentPrepared()方法,跟踪代码后发现它调用EventPublishingRunListener的environmentPrepared()方法,它同样也是执行发送广播的方法:
在这里插入图片描述
它发送广播时eventType为ApplicationEnvironmentPreparedEvent。通过getApplicationListeners(event, type) 得到以下7个listener:
ConfigFileApplicationListener
AnsiOutputApplicationListener
LoggingApplicationListener
ClasspathLoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
FileEncodingApplicationListener

继续看run()方法中的prepareContext()方法,我们看到这里会执行2个方法listeners.contextPrepared(context); 和 listeners.contextLoaded(context);
跟踪代码后发现contextPrepared 没有执行任何代码,所以我们不管它。
在这里插入图片描述
我们看 listeners.contextLoaded(context); 它调用EventPublishingRunListener的contextLoaded()方法,首先会把我们前面加载得到的10个ApplicationListener对应的对象放到context容器中。接着再发送广播,这里的eventType为ApplicationPreparedEvent,通过getApplicationListeners(event, type) 得到以下4个listener:
ConfigFileApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
在这里插入图片描述

接着看run()方法中的refreshContext(context);方法,它里面调用的refresh()方法,看过spring源码的都应该对这个refresh都应该不陌生。它是spring中很重要的一个方法。

在这里插入图片描述

这里我们主要看关于listener事件的处理。我们先看initApplicationEventMulticaster():
在这里插入图片描述
这里主要是获取SimpleApplicationEventMulticaster applicationEventMulticaster对象。

接着看registerListeners()方法,这里会分别把容器中的listener存放在defaultRetriever对象的applicationListeners集合中,defaultRetriever是定义在抽象类AbstractApplicationEventMulticaster中的成员,用来保存所有事件监听器及其beanName
在这里插入图片描述
在这里插入图片描述

再执行finishRefresh(),这里它会先进入EmbeddedWebApplicationContext类的finishRefresh(),然后调用super.finishRefresh();去执行AbstractApplicationContext类的finishRefresh()。

EmbeddedWebApplicationContext类:在这里插入图片描述

AbstractApplicationContext类:
在这里插入图片描述
在AbstractApplicationContext类的finishRefresh()中它会执行publishEvent()方法去SimpleApplicationEventMulticaster类中发送广播,此时的type为ContextRefreshedEvent,getApplicationListeners(event, type) 有如下6个值:
ConfigurationPropertiesBindingPostProcessor
DelegatingApplicationListener
AutoConfigurationReportLoggingInitializer
ClearCacheApplicationListener
SharedMetadataReaderFactoryContextInitializer
ResourceUrlProvider

发送完后又会到EmbeddedWebApplicationContext类的finishRefresh(),此时如果我们得到了一个嵌入式的容器(这里应该指tomcat),那么我们同样会去SimpleApplicationEventMulticaster类中发送广播,此时的type为EmbeddedServletContainerInitializedEvent,getApplicationListeners(event, type) 有如下2个值:
DelegatingApplicationListener
ServerPortInfoApplicationContextInitializer

执行完返回到run()方法中,接着执行listeners.finished(context, null),这里会执行到AbstractApplicationContext类中的publishEvent()方法,最终也会去SimpleApplicationEventMulticaster类中发送广播,此时的type为ApplicationReadyEvent,getApplicationListeners(event, type) 有如下3个值:
BackgroundPreinitializer
DelegatingApplicationListener
SpringApplicationAdminMXBeanRegistrar

至此我们springboot在启动时,事件监听就全部执行完了。容器也启动成功了。

Spring事件汇总

事件Event说明
ContextRefreshedEvent当容器被实例化或refreshed时发布.如调用refresh()方法, 此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化, 所有的容器对象都已准备好可使用. 如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而GenericApplicationContext则不支持)
ContextStartedEvent当容器启动时发布,即调用start()方法, 已启用意味着所有的Lifecycle bean都已显式接收到了start信号
ContextStoppedEvent当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭的容器可以通过start()方法重启
ContextClosedEvent当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启或refresh
RequestHandledEvent这只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布
SpringApplicationEvent与SpringApplication相关的ApplicationEvent的基类
ApplicationStartedEvent该事件尽可能早地发布,只要SpringApplication已启动。它在Environment或ApplicationContext有效之前,但是在ApplicationListener已经注册后发布。不过要小心使用它的内部,在这个早期阶段状态太多,因为它可能在生命周期的后期被修改
ApplicationStartingEventApplicationStartingEvent是ApplicationStartedEvent的父类,ApplicationStartedEvent已经过时了,在1.5版本开始支持ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent当SpringApplication启动时,且Environment首先可用于检查和修改
ApplicationPreparedEvent当SpringApplication启动并且ApplicationContext已完全准备好,但未刷新时发布。Bean定义将被加载,Environment已准备好在此阶段使用
EmbeddedServletContainerInitializedEvent在上下文刷新和EmbeddedServletContainer已就绪后发布。用于获取正在运行的服务器的本地端口。正常情况下,它会被启动,但是监听器可以自由地检查服务器,如果愿意,可以停止和启动它。
ApplicationFailedEvent启动失败时由SpringApplication发布的事件
ApplicationReadyEvent尽可能晚地发布事件,以指示应用程序已准备好为请求提供服务。事件的源是SpringApplication本身,但请注意不要修改其内部状态,因为届时所有初始化步骤都将完成。

版权声明:本文为uk8692原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。