这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
前情提要
在之前的 ApplicationContext 初始化 Spring 容器 一文中,提到 AbstractApplicationContext#refresh
方法是一个非常重要的方法,它包含了 Spring 容器初始化的整个流程。最近的一系列文章都在深入分析这个方法中的每一个步骤的具体原理,本文接着分析 后续的流程,也就是refresh
方法中的这几行代码:
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
复制代码
初始化事件广播器
先看initApplicationEventMulticaster
方法,从方法名可以看出,它的作用是初始化时间广播器。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
复制代码
这段逻辑跟上一篇介绍的 MessageSource 的初始化逻辑比较相似。先从beanFactory
中查找是不是包含名为applicationEventMulticaster
的 Bean。如果有的话,将其获取到并赋值给当前上下文的applicationEventMulticaster
成员变量。如果没有的话,创建一个 SimpleApplicationEventMulticaster 并赋值给applicationEventMulticaster
成员变量,再注册到beanFactory
中。
顺便简单介绍一下 Spring 的事件广播器。Spring 提供了一个基于事件(Event)的 Pub/Sub 机制,这个事件广播器存在与 Spring 的上下文当中。Spring 还有另外一个与此相关的组件叫做事件监听器(ApplicationListener),事件监听器作为 Bean 注册在 Spring 的容器中,每个事件监听器都有其监听的事件。当通过 Spring 的上下文发布一个事件之后,事件广播器就会获取到所有的事件监听器,并通知监听了这个事件的事件监听器。
本文的后半部分会详细介绍事件监听器,关于如何发布事件,也会在之后的文章介绍道。
空的onFresh方法
接下来看第二个方法调用,也就是onRefresh
方法。
/**
* Template method which can be overridden to add context-specific refresh work.
* Called on initialization of special beans, before instantiation of singletons.
* <p>This implementation is empty.
* @throws BeansException in case of errors
* @see #refresh()
*/
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
复制代码
这是一个protected
修饰的空模版方法,在方法的注释中,Spring 已经告诉我们它的作用,以及被调用的时机。这个方法在一些特定的 Bean 被实例化之后,普通的单例 Bean 初始化之前调用。子类可以继承这个方法来实现一些特定的逻辑。
这个方法属于 Spring 提供的扩展点。
注册事件监听器
最后看registerListeners
方法。前面在初始化事件广播器的部分,已经介绍了事件监听器在整个事件广播相关环节的作用,这里有必要看一下事件监听器的具体代码。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
复制代码
它是一个函数式接口,其中的onApplicationEvent
方法用于处理事件。也就是说,事件广播器将事件通知给事件监听器,其实就是调用了onApplicationEvent
方法。监听器需要实现这个方法,来定义监听到之间之后要执行的逻辑。
下面看它们是如何被注册的。
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
复制代码
注册事件监听器,其实就是把事件监听器添加到事件广播器的监听器列表中,因为,当有事件被发布的时候,是由事件广播器来通知事件监听器的。上面的代码,大体分为三个部分,我们分别来看:
- 首先,通过
getApplicationListeners
方法,将上下文的成员变量applicationListeners
中的监听器注册到广播器中。
- 然后,通过
getBeanNamesForType
方法,从beanFactory
中获取到所有 ApplicationListener 类型的 Bean 的名字,添加到广播器中。
- 最后,Spring 上下文的
earlyApplicationEvents
成员变量中保存了一些在事件广播器初始化之前就已经被发布,但是还未广播的事件,通过调用事件广播器的multicastEvent
方法对其进行广播。
事件广播器是如何保存监听器的
以上步骤中的第一步是将监听器实例交给事件广播器,而第二步是将监听器的beanName
交给事件监听器,这里有比要介绍一下事件监听器是如何处理的。
在事件广播器的内部,有一个 ListenerRetriever 类型的成员变量defaultRetriever
,是在事件广播器被创建的时候就初始化的。
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
复制代码
当有监听器被注册到事件广播器中的时候,实际上是交给了defaultRetriever。在defaultRetriever中,有两个集合类型的成员变量:
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
复制代码
它们分别保存着已经被实例化的监听器对象和未被初始化的监听器的beanName
。当事件广播器需要获取所有监听器的时候,ListenerRetriever 就回使用applicationListenerBeans
中保存的beanName
把所有的实例都获取到,在跟applicationListeners
中的所有实例合并一起返回。
事件是如何被广播的
分析完这个问题,再看一下事件广播器是如何广播事件的,也就是multicastEvent
的具体原理。
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
复制代码
这里的逻辑很简单,先根据event
获取到支持处理此类行事件的所有 ApplicationListener,然后通过invokeListener
方法,执行监听器中的处理逻辑。
再进入invokeListener
方法看执行的过程。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
} catch (Throwable err) {
errorHandler.handleError(err);
}
} else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
/* 省略异常处理的逻辑 */
}
}
复制代码
以上代码格式略做了调整,其实这里最核心的代码只有一行,就是调用了监听器的onApplicationEvent
方法。
至此,这部分代码就分析完了。
后续
本文主要介绍了事件广播相关的广播器的初始化和监听器的注册,以及事件是如何广播的,至于事件广播器什么时候会广播一个事件,后面的路程中会有事件发布的流程,因此,到之后在做详细介绍。后续会继续分析 Spring 容器初始化的代码。