Spring 源码阅读 16:初始化事件广播器 & 注册事件监听器

简介: 本文分析 Spring 上下文初始化过程中, 初始化事件广播器和注册事件监听器的过程。它们是 Spring 框架提供的基于 Pub/Sub 机制的事件发布和订阅模型。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 15:初始化 MessageSource

前情提要

在之前的 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方法,从方法名可以看出,它的作用是初始化时间广播器。

protectedvoidinitApplicationEventMulticaster() {
ConfigurableListableBeanFactorybeanFactory=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=newSimpleApplicationEventMulticaster(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()*/protectedvoidonRefresh() throwsBeansException {
// For subclasses: do nothing by default.}

这是一个protected修饰的空模版方法,在方法的注释中,Spring 已经告诉我们它的作用,以及被调用的时机。这个方法在一些特定的 Bean 被实例化之后,普通的单例 Bean 初始化之前调用。子类可以继承这个方法来实现一些特定的逻辑。

这个方法属于 Spring 提供的扩展点。

注册事件监听器

最后看registerListeners方法。前面在初始化事件广播器的部分,已经介绍了事件监听器在整个事件广播相关环节的作用,这里有必要看一下事件监听器的具体代码。

@FunctionalInterfacepublicinterfaceApplicationListener<EextendsApplicationEvent>extendsEventListener {
/*** Handle an application event.* @param event the event to respond to*/voidonApplicationEvent(Eevent);
}

它是一个函数式接口,其中的onApplicationEvent方法用于处理事件。也就是说,事件广播器将事件通知给事件监听器,其实就是调用了onApplicationEvent方法。监听器需要实现这个方法,来定义监听到之间之后要执行的逻辑。

下面看它们是如何被注册的。

protectedvoidregisterListeners() {
// 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 (StringlistenerBeanName : 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 (ApplicationEventearlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

注册事件监听器,其实就是把事件监听器添加到事件广播器的监听器列表中,因为,当有事件被发布的时候,是由事件广播器来通知事件监听器的。上面的代码,大体分为三个部分,我们分别来看:

  1. 首先,通过getApplicationListeners方法,将上下文的成员变量applicationListeners中的监听器注册到广播器中。
  2. 然后,通过getBeanNamesForType方法,从beanFactory中获取到所有 ApplicationListener 类型的 Bean 的名字,添加到广播器中。
  3. 最后,Spring 上下文的earlyApplicationEvents成员变量中保存了一些在事件广播器初始化之前就已经被发布,但是还未广播的事件,通过调用事件广播器的multicastEvent方法对其进行广播。

事件广播器是如何保存监听器的

以上步骤中的第一步是将监听器实例交给事件广播器,而第二步是将监听器的beanName交给事件监听器,这里有比要介绍一下事件监听器是如何处理的。

在事件广播器的内部,有一个 ListenerRetriever 类型的成员变量defaultRetriever,是在事件广播器被创建的时候就初始化的。

privatefinalListenerRetrieverdefaultRetriever=newListenerRetriever(false);

当有监听器被注册到事件广播器中的时候,实际上是交给了defaultRetriever。在defaultRetriever中,有两个集合类型的成员变量:

publicfinalSet<ApplicationListener<?>>applicationListeners=newLinkedHashSet<>();
publicfinalSet<String>applicationListenerBeans=newLinkedHashSet<>();

它们分别保存着已经被实例化的监听器对象和未被初始化的监听器的beanName。当事件广播器需要获取所有监听器的时候,ListenerRetriever 就回使用applicationListenerBeans中保存的beanName把所有的实例都获取到,在跟applicationListeners中的所有实例合并一起返回。

事件是如何被广播的

分析完这个问题,再看一下事件广播器是如何广播事件的,也就是multicastEvent的具体原理。

@OverridepublicvoidmulticastEvent(ApplicationEventevent) {
multicastEvent(event, resolveDefaultEventType(event));
}
@OverridepublicvoidmulticastEvent(finalApplicationEventevent, @NullableResolvableTypeeventType) {
ResolvableTypetype= (eventType!=null?eventType : resolveDefaultEventType(event));
Executorexecutor=getTaskExecutor();
for (ApplicationListener<?>listener : getApplicationListeners(event, type)) {
if (executor!=null) {
executor.execute(() ->invokeListener(listener, event));
      }
else {
invokeListener(listener, event);
      }
   }
}

这里的逻辑很简单,先根据event获取到支持处理此类行事件的所有 ApplicationListener,然后通过invokeListener方法,执行监听器中的处理逻辑。

再进入invokeListener方法看执行的过程。

protectedvoidinvokeListener(ApplicationListener<?>listener, ApplicationEventevent) {
ErrorHandlererrorHandler=getErrorHandler();
if (errorHandler!=null) {
try {
doInvokeListener(listener, event);
      } catch (Throwableerr) {
errorHandler.handleError(err);
      }
   } else {
doInvokeListener(listener, event);
   }
}
@SuppressWarnings({"rawtypes", "unchecked"})
privatevoiddoInvokeListener(ApplicationListenerlistener, ApplicationEventevent) {
try {
listener.onApplicationEvent(event);
   } catch (ClassCastExceptionex) {
/* 省略异常处理的逻辑 */   }
}

以上代码格式略做了调整,其实这里最核心的代码只有一行,就是调用了监听器的onApplicationEvent方法。

至此,这部分代码就分析完了。

后续

本文主要介绍了事件广播相关的广播器的初始化和监听器的注册,以及事件是如何广播的,至于事件广播器什么时候会广播一个事件,后面的路程中会有事件发布的流程,因此,到之后在做详细介绍。后续会继续分析 Spring 容器初始化的代码。

目录
相关文章
|
9天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
22 0
|
13天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
3天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
4天前
|
消息中间件 安全 Java
探索|Spring并行初始化加速的思路和实践
作者通过看过的两篇文章发现实现Spring初始化加速的思路和方案有很多类似之处,通过本文记录一下当时的思考和实践。
|
6天前
|
XML Java 数据格式
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
6 0
|
6天前
|
XML Java 数据格式
手写spring第五章-简化用户操作,基于xml完成bean容器初始化
手写spring第五章-简化用户操作,基于xml完成bean容器初始化
10 0
|
6天前
|
Java Shell 测试技术
Spring源码搭建教程
Spring源码搭建教程
11 0
|
8天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
14天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
25 0
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
46 0