前言
Spring 从 3.x 开始支持事件机制。在 Spring 的事件机制中,我们可以令一个事件类继承 ApplicationEvent 类,然后将实现了 ApplicationListener 的 Bean 注册到 spring 容器,最后向 ApplicationEventPublisher 推送事件对象即可令所有订阅者收到事件。在 4.2 以后,甚至不需要实现 ApplicationListener 接口,仅需在 Bean 中方法标记 @EventListener 注解即可。
笔者将基于 Spring 源码的 5.2.x 分支,分析该功能是如何实现的。
本文是其中的第一篇文章,将分析广播器与监听的是如何被初始化,并完成注解流程的。
在开始前,推荐先阅读前文了解 Spring 容器的初始化过程与 BeanFactory 中 Bean 的创建,如果可能,还可以了解一点 Spring 的注解机制,这将更有利于流程与一些代码的理解。
相关文章:
一、广播器的创建
在前文,我们知道容器的初始化是通过 AbstractApplicationContext.refresh() 方法完成的,事件机制的相关组件同样也离不开容器,因此事件系统的初始化也通过该方法完成。
AbstractApplicationContext.initApplicationEventMulticaster() 是第一步,它的作用很简单:
如果当前 BeanFactory 有名为 “applicationEventMulticaster” 的 ApplicationEventMulticaster,就把它设置为当前上下文的事件广播器,否则就创建并在 BeanFactory 中注册一个SimpleApplicationEventMulticaster 实例作为当前上下文的事件广播器。
1 | protected void initApplicationEventMulticaster() { |
二、编程式监听器的注册
在 4.2 及以前版本,监听器需要显式的实现 ApplicationListener 接口,我们管这种监听器叫做编程式监听器。
针对编程式监听器,有两种注册途径:
- 在上下文初始化过程中通过
AbstractApplicationContext.registerListeners()方法完成注册; - 在 Bean 初始化后,通过
ApplicationListenerDetector这个后处理器的postProcessAfterInitialization方法将其注册;
1、由上下文直接注册
编程式监听器在 AbstractApplicationContext.registerListeners() 这个方法的调用过程中被注册到注册广播器中,这一块代码逻辑也很简单:
- 向事件广播器注册已经被注册的
BeanFactroy中,且实现了ApplicationListener接口的监听器; - 向事件广播器注册还没有被实例化的监听器的
BeanName; - 发布一些早期事件;
1 | protected void registerListeners() { |
我们需要注意的是,在这一步,虽然向广播器注册了监听器,但是实际上这只是一种关系,真正的监听器实例不一定有被创建出来。
不过在如果上下文中存在“早期事件”,则会触发广播,此时调用 ApplicationEventMulticaster.multicastEvent() 将会提前触发广播器中那些监听器的初始化,否则按正常情况这些将等到上下文主动初始化 BeanFactory 中全部非懒加载 Bean 的时候才会一并初始化。
2、通过后处理器注册
在 AbstractApplicationContext.refresh 方法中,在 prepareBeanFactory 这一步初始化 BeanFactory 时,会默认向工厂注册一个名为 ApplicationListenerDetector Bean 后处理器。
该处理器同时实现了 MergedBeanDefinitionPostProcessor 和 DestructionAwareBeanPostProcessor 和 BeanPostProcessor 三大处理器接口,这分别对应了它处理监听器的三个步骤:
MergedBeanDefinitionPostProcessor:当 Bean 实例化前,对BeanDefinition进行进行后处理时,它会记录这些实现了ApplicationEventListener接口的 Bean 的名称;BeanPostProcessor:当 Bean 初始化后,它会根据记录的 BeanName 从容器中找到对应的Bean实例,然后将其注册到上下文的广播器中;DestructionAwareBeanPostProcessor:当 Bean 被销毁时,它会根据记录的 BeanName 将对应的 Bean 从广播器中移除;
三、注解式监听器的注册
在 4.2 版本以后,我们可以通过在成员方法上添加 @EventListener 或者 @TransactionalEventListener 注解的方法声明一个监听器,我们管这种监听器叫做注解式监听器。
实际上,由于注解式监听器的类上没有注解或接口作为标识,因此无法直接从 BeanFactory 中查找,所以它的注册显然不能与编程式监听器一样,在 AbstractApplicationContext.registerListeners() 通过从 BeanFactory 中直接找到然后注册。
和 3.0 以后支持的一些注解式配置的原理一样,@EventListener 是通过 EventListenerMethodProcessor 这个特殊的后置处理器完成注册的。
EventListenerMethodProcessor 实现的接口如下:
1 | public class EventListenerMethodProcessor |
其中, SmartInitializingSingleton 和 BeanFactoryPostProcessor 接口非常直观的告诉了我们它被调用的时机:
BeanFactoryPostProcessor:在上下文初始化的时候,通过AbstractApplicationContext.invokeBeanFactoryPostProcessors这个方法跟其他BeanFactory的后置处理器被一起集中调用;SmartInitializingSingleton:在这个Bean完成初始化的时候;
接下来我们通过处理器分析注解式监听器的注册流程。
1、监听器方法处理器的注册
根据前文,我们知道容器在初始化过程中,通过 AbstarctApplicationContext.obtainFreshBeanFactory 创建新 BeanFactory 的时候,最终会一路绕到 AbstractRefreshableApplicationContext.loadBeanDefinitions 这个方法上,通过这个方法上下文会为自己的 BeanFactory 提前加载好 BeanDefinition。
而这个抽象方法在不同的上下文会有不同的实现,但是基本都要通过不同的 BeanDefinitionReader 去完成这个过程。
支持注解式配置的上下文会用 AnnotatedBeanDefinitionReader 去读取配置的时候,会通过 AnnotationConfigBeanDefinitionParser 将配置信息解析为具体的 BeanDefinition 。而 Spring 就在这一步将默认配置的一些 Bean 的 BeanDefinition 给加上了。
实际上,不止 EventListenerMethodProcessor ,几乎所有针对 Spring 注解的后置处理器都是通过这种方式注册到 BeanFactory 的。
具体代码参见 AnnotationConfigUtils.registerAnnotationConfigProcessors 方法:
1 | public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( |
2、获取监听器工厂
当 EventListenerMethodProcessor 被作为一个 BeanFactoryPostProcessor 被调用时,它会从 BeanFactory 中收集所有实现了 EventListenerFactory 接口的 Bean,然后记录在成员变量 eventListenerFactories 中:
1 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
而监听器工厂这个类作用也显而易见,他用于把被注解的方法适配为监听器对象:
1 | public interface EventListenerFactory { |
值得一提的是,由于注册 EventListenerMethodProcessor 的时候也会默认支持一个名为 :"org.springframework.context.event.internalEventListenerFactory" 的 DefaultEventListenerFactory,这保证至少有一个保底的监听器工厂。
EventListenerFactory 提供两个默认的实现:
-
DefaultEventListenerFactory:默认的实现,支持处理所有被@EventListener注解的方法,会将方法适配成类型为
ApplicationListenerMethodAdapter的监听器; -
TransactionalEventListenerFactory:支持 Spring 事务机制的监听器的工厂, 用于处理被@TransactionalEventListener注解的方法,会将方法适配成类型为
ApplicationListenerMethodTransactionalAdapter的监听器;
3、将方法适配为监听器
当 EventListenerMethodProcessor 作为一个 SmartInitializingSingleton 被调用的时候:
1 | public void afterSingletonsInstantiated() { |
抛开对代理对象的一些检验和处理,我们直接看看 processBean 方法:
1 | private void processBean(final String beanName, final Class<?> targetType) { |
Spring 在这一步主要趁着 EventListenerMethodProcessor 在 BeanFactory 中初始化的时候干了两件事:
- 检查
BeanFactory中的所有的Bean,筛选出其中有成员方法直接或间接带有@EventListener注解的Bean; - 将此类
Bean的方法通过EventListenerFactory封装为ApplicationListener对象; - 然后将这些转换后得到的
ApplicationListener注册到上下文中的广播器中;
此外,这里有一个比较有意思的细节,就是由于 @EventListener 注解是支持在 condition 中通过 SpEL 表达式进行一些判断的,因此在这一步,针对默认的监听适配器实现 ApplicationListenerMethodAdapter ,提供了一个 init 方法用于把 SpEL 表达式解析器塞进去:
1 | if (applicationListener instanceof ApplicationListenerMethodAdapter) { |
换而言之,如果我们希望让 @EventListener.condition 支持更多功能,就可以在这个地方动点手脚,比如向 SpEL 表达式上下文注册更多变量。
4、监听器的注册
上一节中提到,在 EventListenerMethodProcessor.processBean 将方法转换为 ApplicationListener 后会将其注入广播器:
1 | public void addApplicationListener(ApplicationListener<?> listener) { |
而 AbstractApplicationContext 会将该方法代理到内部持有的广播器实例的 ApplicationEventMulticaster.addApplicationListener 方法:
1 | public void addApplicationListener(ApplicationListener<?> listener) { |
该方法最终将监听器添加到广播器持有的 DefaultListenerRetriever 对象实例中,跟已经注册到其中的编程式监听器一起,以待后续使用。
四、监听器工厂
通过上文,我们知道注解式监听器依赖监听器工厂 EventListenerFactory 将 Bean 中的注解方法转为 ApplicationListener 实例。
实际上,我们知道 spring 除了支持 @EventListener 注解外,还提供了 @TransactionalEventListener 注解,用于注册支持事务的注解式监听器,因此 EventListenerFactory 实际上也提供了两类工厂分别用于支持这两种实现:
-
DefaultEventListenerFactory:默认的实现,支持处理所有被@EventListener注解的方法,会将方法适配成类型为
ApplicationListenerMethodAdapter的监听器; -
TransactionalEventListenerFactory:支持 Spring 事务机制的监听器的工厂, 用于处理被@TransactionalEventListener注解的方法,会将方法适配成类型为
ApplicationListenerMethodTransactionalAdapter的监听器;
1、通用监听器工厂
通用监听器工厂的代码及其简单,它的特点如下:
- 支持处理任何方法:
supportsMethod方法固定返回true; - 总是最晚被执行:
getOrder默认返回Ordered.LOWEST_PRECEDENCE; - 总是将注解方法适配为
ApplicationListenerMethodAdapter类型的监听器;
1 | public class DefaultEventListenerFactory implements EventListenerFactory, Ordered { |
2、事务监听器工厂
事件监听器工厂代码也并不复杂,相比 DefaultEventListenerFactory,它的特点如下:
- 默认比
DefaultEventListenerFactory更先被调用:getOrder默认返回 50,比DefaultEventListenerFactory返回的Ordered.LOWEST_PRECEDENCE值更小; - 仅支持处理直接或间接被
@TransactionalEventListener注解的方法; - 总是将注解方法适配为
ApplicationListenerMethodTransactionalAdapter类型的监听器;
1 | public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered { |
总结
当 Spring 容器启动,上下文调用 AbstractApplicationContext.refresh 方法对其进行初始化时,Spring 事件机制的两个核心组件:广播器、监听器也在该过程完成初始化。
-
在
AbstractApplicationContext.initApplicationEventMulticaster这一步,Spring 为上下文创建并挂载了广播器ApplicationEventMulticaster的实例; -
在
AbstractApplicationContext.registerListeners这一步,Spring 将容器中所有实现了ApplicationListener接口的Bean注册到广播器中; -
而在
AbstractApplicationContext.finishBeanFactoryInitialization这一步,Spring 会初始化容器中所有非懒加载的Bean,此时会一并实例化EventListenerMethodProcessor这个Bean后置处理器:-
这个
Bean由上下文在准备BeanFactory时,调用loadBeanDefinitions方法时注册到容器; -
由于
EventListenerMethodProcessor本身实现了SmartInitializingSingleton接口,因此实例化后会触发其回调函数,此时EventListenerMethodProcessor会把容器中所有的Bean都拿出来解析; -
若
Bean中存在直接或间接被@EventListener注解的方法,则会将其取出,并通过EventListenerFactory将其适配为对应的ApplicationListener实例,然后再将适配完的监听器注册到容器中; -
由于 Spring 除了
@EventListener注解还提供了支持事务的@TransactionalEventListener注解,因此提供了两类监听器工厂:DefaultEventListenerFactory:默认的实现,支持处理所有被@EventListener注解的方法;TransactionalEventListenerFactory:支持 Spring 事务机制的监听器的工厂, 用于处理被@TransactionalEventListener注解的方法;
-
至此,当 AbstractApplicationContext.refresh 执行完毕,即上下文初始化完成后,广播器与所有编程式或注解式监听器皆初始化完毕,并且完成了注册。