SpringBoot版本:2.0.2.RELEASE
SpringFramework版本:5.0.6.RELEASE
文章目录
SpringFramework的监听者这一块设计得也是挺有意思的。监听者的核心设计思想就是通知者模式。随着SpringFramework版本的不断演变,整个事件与监听机制都在不断的优化。监听者可以仅接受自己感兴趣的事件。SpringBoot是基于SpringFramework发展而来的,它的监听者也是融入到SpringFramework的机制里。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200812082953237.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l5Yl9neg==,size_16,color_FFFFFF,t_70#pic_center)
上图是SpringFramework监听者接口方面的UML图。除了EventListener接口外,ApplicationListener、GenericApplicationListener和SmartApplicationListener接口都有自己的实现类。那么,划分不同的接口背后想表达什么意思呢?后面我们逐步解开这个迷。
监听者从何而来
在SpringBoot的工程里,SpringFramework的监听者有两部分来源,一些SpringBoot带来的,别一些是SpringFramework原生。
来自SpringBoot的监听器
在《SpringFramework事件与监听机制(事件)》文章里,我们看到在SpringBoot的启停过程中,SpringBoot的事件是最早发布的,到了中间的部分才发布ContextRefreshedEvent。那么,是不是SpringBoot的事件发布给SpringBoot的监听者,Spring的事件发布给Spring的监听者呢?我们从代码的层面来观察。
根据《SpringBoot事件与监听机制》,SpringBoot通过EventPublishingRunListener发布事件,我们把目光放在EventPublishingRunListener的几个发布事件的方法。它的方法的顺序跟SpringBoot发布各阶段事件的顺序是一致的。
从代码看,SpringBoot发布ApplicationStartingEvent和ApplicationEnvironmentPreparedEvent都有着相同的逻辑:
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
在发布ApplicationPreparedEvent的contextLoaded方法就有些不一样了:
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
它会先将SpringBoot的监听器加到SpringFramework的context里。然后才发布ApplicationPreparedEvent事件。
后面的ApplicationStartedEvent和ApplicationReadyEvent都是通过SpringFramework的ConfigurableApplicationContext来发布。
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationReadyEvent(this.application, this.args, context));
}
在这里,我们至少看到一个发布者身份转换的过程。而转换的准备阶段就在发布ApplicationPreparedEvent的时候,此时将SpringBoot的监听器都“告诉”SpringFramework的ConfigurableApplicationContext了。用“告诉”这词,因为SpringBoot自己仍然知道监听者的地址,而不是说交给SpringFramework后自己就完全不管了。
我们得往回看下代码,在发布ApplicationPreparedEvent的时候是处于什么阶段。SpringApplication#run方法代码片段如下:
public ConfigurableApplicationContext run(String... args) {
....
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] {
ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
....
prepareContext方法里就会发布ApplicationPreparedEvent事件。从方法的名字来看,就是为context做准备。而这方法的上两条命令ConfigurableApplicationContext才被创建。这方法后面紧跟着就是refreshContext方法,在该方法里,最终是由SpringFramework的ConfigurableApplicationContext发布ContextRefreshedEvent事件。也就是说,在context的准备阶段会做一些事情并且知到了SpringBoot的监听器,将它们放进自己的库里。后续ConfigurableApplicationContext发布的事件也会被SpringBoot的监听者监听到。
SpringBoot后面发布ApplicationStartedEvent和ApplicationReadyEvent的事件虽然表面上通过SpringApplicationRunListeners发布,实质上最终由ConfigurableApplicationContext发布。简单地说,在发布ApplicationPreparedEvent时ConfigurableApplicationContext还未成熟,所以此时没有让它发布事件。到了ContextRefreshedEvent时ConfigurableApplicationContext已经作好了准备(ConfigurableApplicationContext#refresh方法已完成),所以就让它来发布。后来的事件也都由ConfigurableApplicationContext来发布了。
上面的论述有点啰嗦,但结论就是并非一直是SpringBoot发布的事件就只有SpringBoot的监听者收到,SpringFramework发布的事件就只有SpringFramework的监听者收到。事实上是从ContextRefreshedEvent事件开始,ConfigurableApplicationContext发布的事件也能够被SpringBoot的监听者收到。
来自SpringFramework的监听器
AbstractApplicationContext,是众多的ApplicationContext子类的父类,而它实现了ConfigurableApplicationContext接口,接口的refresh方法在AbstractApplicationContext已实现,如果子类没重载就继承了它的refresh方法。目前看SpringFramework的其他Context子类没重载这条方法。
在Abstra