- 观察者模式直白解释:可理解为订阅和通知的过程,即订阅者(或者监听者、观察者)向注册和通知中心注册自己以及关注的事件,发布者(或者被观察者、通知者)向注册和通知中心发布事件消息,委托注册和通知中心向关注了该事件的观察者发布通知。通过注册和通知中心解耦了观察者和发布者,观察者不需要关心发布者是谁,只需关注自己对什么感兴趣;发布者不需关心观察者是谁,只需关注自己及时的发布通知。相关类图不再赘述,网上有很多,下图是观察者模式的交互方式,旨在便于理解。

Spring中的事件编程模型就是观察者设计模式的实现,SpringBoot正是利用了Spring的事件编程模型来实现了Application启动过程中的相关事件的广播。
Spring中观察者模式核心类如下:
- ApplicationListener:应用监听者,即观察者,继承了JDK中EventListener,当监听的事件发生后,该类中唯一方法onApplication会被回调;
- ApplicationEvent:事件抽象类,继承自JDK的EventObject,Spring及SpringBoot中所有事件都是该类的子类;
- ApplicationEventMulticaster:事件注册和广播中心,用于事件监听器的注册和事件的广播;
- ApplicationContext:Spring的IOC容器,同时也是发布者,因为ApplicationContext继承了ApplicationEventPublisher,通过publishEvent(Object event)方法发布事件。
分析SpringBoot的启动过程会发现,大量的代码都是在做事件通知和扩展点设置,他们近乎喧宾夺主,占据了SpringBoot启动过程的大半江山,抛去这些扩展点,剩下的核心逻辑无非只是初始化并准备ApplicationContext容器了。关于SpringBoot的启动过程和其他扩展点解析,笔者会在后续发文中逐一解析,现在暂且关注事件模型。
SpringBoot启动核心代码,其中注释标注出了事件通知代码
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//向注册和通知中心注册SpringBoot监听者,并返回注册和通知中心实例
SpringApplicationRunListeners listeners = getRunListeners(args);
//向注册和通知中心发布应用正在启动的事件通知
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备配置环境,并向通知和注册中心发布环境准备好了的事件通知
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//向通知和注册中心发布应用启动启动完毕的事件通知
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//处理运行失败异常,并向通知和注册中心发布应用启动失败事件通知
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
//向通知和注册中心发布引用正在运行的事件通知
listeners.running(context);
return context;
}
其中getRunListeners方法,会借助SpringFactoriesLoader机制从spring.factories文件中加载SpringApplicationRunListener类型的实现类,该类为SpringBoot提供的用于监听SpringBoot启动状态的观察者类,其唯一实现为EventPublishingRunListener,位于spring-boot-2.0.0.RELEASE.jar包中META-INF下spring.factories核心代码如下:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
SpringFactoriesLoader机制正是SpringBoot自动装配的核心逻辑,关于其原理可以阅读笔者的这篇关于starter的原理解析:拒绝花里胡哨,极简springboot starter自定义及原理,那么SpringBoot中的监听者注册就是通过这种方式注册到注册和通知中心的。
SpringBoot的注册和通知中心为SpringApplicationRunListeners,其实这个名字不够语义化,实际上该类包含了一个SpringApplicationRunListener列表和事件通知方法。
SpingBoot的发布者为SpringApplication实体,SpringBoot的启动核心流程均定义在该类下。
SpringBoot从ApplicationEvent扩展出了SpringApplicationEvent事件抽象类,其子类有ApplicationStartingEvent、ApplicationReadyEvent等,均代表了应用启动状态
那么SpringBoot的事件机制是怎样利用Spring的事件模型的呢,答案就在EventPublishingRunListener这个SpringBoot的监听者中,原来当SpringApplication向注册和通知中心发布启动状态事件后,事件会通知到该观察者,但是该观察者接收到事件后并没做其他事情,仅仅是将该事件通过Spring中的广播者SimpleApplicationEventMulticaster再将事件广播到Spring中的监听者,源码及注释如下:
@Override
public void starting() {
//initialMulticaster即为Spring中的广播者
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
那么这些Spring监听者是在什么时候被注册进去的呢,其实在SpringBoot启动之初,会构建SpringApplication对象,这里同样是借助SpringFactoriesLoader机制,来加载SpringBoot提供的一些ApplicationListener的监听者,源码及注释如下:
//SpringApplication构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//通过SpringFactoriesLoader机制注册Spring的监听者
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
位于spring-boot-2.0.0.RELEASE.jar包中META-INF下spring.factories核心代码如下:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
至此,SpringBoot中的观察者模式原理就讲完了,SpringBoot中的监听者可以自定义,并通过SpringFactoriesLoader机制注册,您可以试下以检验自己的理解程度。
如果您熟悉观察者模式这一内功心法,当在研究SpringBoot及Spring源码招式的时候,相信会理解起来事半功倍,希望这篇文章对您理解原理有所帮助。