Spring IoC 源码系列(三)Spring 事件发布机制原理分析
在 IoC 容器启动流程中有一个 finishRefresh
方法,具体实现如下:
protected void finishRefresh() {clearResourceCaches();initLifecycleProcessor();getLifecycleProcessor().onRefresh();// 向所有监听 ContextRefreshedEvent 事件的监听者发布事件publishEvent(new ContextRefreshedEvent(this));LiveBeansView.registerApplicationContext(this);}
这里我们只关注 publishEvent
方法,这个方法用于发布 IoC 刷新完成事件,事件时如何发布的呢?下面我们一起来看一下。
一、原理分析
@Overridepublic void publishEvent(ApplicationEvent event) {publishEvent(event, null);}protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");// Decorate event as an ApplicationEvent if necessaryApplicationEvent applicationEvent;// 根据事件类型进行包装if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;} else {applicationEvent = new PayloadApplicationEvent<>(this, event);if (eventType == null) {eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}// Multicast right now if possible - or lazily once the multicaster is initializedif (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);} else {// 获取 ApplicationEventMulticaster,将事件广播出去getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// Publish event via parent context as well...// 判断是否存在父容器,如果存在则将事件也发布到父容器的监听者if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);} else {this.parent.publishEvent(event);}}}
通过上面方法可以看出 Spring 的事件是通过 ApplicationEventMulticaster
广播出去的,这个 ApplicationEventMulticaster
在 IoC 启动流程 initApplicationEventMulticaster
方法中初始化。如果该容器还存在父容器,那也会以同样的形式将事件发布给父容器的监听者。
@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {// 解析事件类型ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));// 根据事件与事件类型获取所有监听者for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {// 获取异步执行器Executor executor = getTaskExecutor();if (executor != null) {// 如果执行器部位 null,则异步执行将事件发布给每一个监听者executor.execute(() -> invokeListener(listener, event));}else {// 同步发布事件invokeListener(listener, event);}}}
发布事件前会先获取所有已注册的监听器,而监听器早已在 IoC 启动流程的 registerListeners
方法中注册。获取到所有事件监听器之后,就可以进行事件发布了。发布的时候分为异步执行与顺序执行,默认情况下 executor
是没有初始化的,因此是顺序执行。
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);}}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {// 执行监听器的 onApplicationEvent 方法listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass())) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception and just log a debug message.Log logger = LogFactory.getLog(getClass());if (logger.isTraceEnabled()) {logger.trace("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}}
到这里 Spring 的事件通知机制流程就结束了,总的来说还是比较好理解的。
二、事件通知 demo
了解了事件通知机制的基本原理后,下面我们来写个 demo 体验一下监听器是如何使用的。参考自:Event事件通知机制
// 定义一个 Event
public class EventDemo extends ApplicationEvent {private static final long serialVersionUID = -8363050754445002832L;private String message;public EventDemo(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}// 定义一个监听器1
public class EventDemo1Listener implements ApplicationListener<EventDemo> {public void onApplicationEvent(EventDemo event) {System.out.println(this + " receiver " + event.getMessage());}
}// 定义一个监听器2
public class EventDemo2Listener implements ApplicationListener<EventDemo> {public void onApplicationEvent(EventDemo event) {System.out.println(this + " receiver " + event.getMessage());}
}// 定义一个事件发布者
public class EventDemoPublish {public void publish(ApplicationEventPublisher applicationEventPublisher, String message) {EventDemo eventDemo = new EventDemo(this, message);applicationEventPublisher.publishEvent(eventDemo);}
}
在 XML 中配置 bean:
<bean id="eventDemoPublish" class="com.jas.mess.event.EventDemoPublish"/><bean id="eventDemo1Listener" class="com.jas.mess.event.EventDemo1Listener"/><bean id="eventDemo2Listener" class="com.jas.mess.event.EventDemo2Listener"/>
编写测试类:
@Testpublic void eventTest() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);applicationContext.getBean("eventDemoPublish", EventDemoPublish.class).publish(applicationContext, "hello world");}
控制台输出:
不知道你有没有注意到,在发布事件的时候我们传的发布者是 applicationContext
,applicationContext
本身继承自 ApplicationEventPublisher
接口,因此它本身也是一个事件发布者。
参考阅读
Event事件通知机制 by wangqi
Spring IoC 源码系列(三)Spring 事件发布机制原理分析相关推荐
- Spring IoC 源码系列(五)getBean 流程分析
一.FactoryBean 用法讲解 在分析源码流程之前,我们先来看一下 FactoryBean,乍一看这家伙和 BeanFactory 很像,它们都可以用来获取 bean 对象,简单来说 Facto ...
- Spring IoC 源码系列(一)BeanDefinition 初始化与注册
一.BeanDefinition 1.1 什么是 BeanDefinition 在一般的 Spring 项目中,主要通过 XML 的方式配置 bean,而 BeanDefinition 就是 XML ...
- Spring IoC 源码系列(四)bean创建流程与循环依赖问题分析
创建单例 bean 的代码细节在 org.springframework.beans.factory.support.AbstractBeanFactory#getBean 中,getBean 顾名思 ...
- JVM源码系列:ThreadMXBean 打出堆栈信息原理分析
我们通常会使用工具jstack 去跟踪线程信息,其如何实现使用attach 的方式还是ptrace 的方式,这些可以去参考本人的博客的其他文章. 但这些方式都是外部使用的方式,如何直接使用java代码 ...
- Spring IoC 源码导读
源码记录:spring-framework-5.1.7-source-code-read 文章导读 Spring IoC 源码系列(一)BeanDefinition 初始化与注册 Spring IoC ...
- Spring IoC源码:getBean 详解
文章目录 Spring源码系列: 前言 正文 方法1:getObjectForBeanInstance 方法2:getObjectFromFactoryBean 方法3:doGetObjectFrom ...
- Spring读源码系列之AOP--03---aop底层基础类学习
Spring读源码系列之AOP--03---aop底层基础类学习 引子 Spring AOP常用类解释 AopInfrastructureBean---免被AOP代理的标记接口 ProxyConfig ...
- Spring AOP 源码系列(一)解析 AOP 配置信息
在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读. 关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐 ...
- 【Spring源码系列】Spring注解扫描-@ComponentScan底层原理解读
这里写目录标题 前言 一.Spring扫描-@ComponentScan注解介绍 @ComponentScan作用 @ComponentScan重要参数 二.Spring扫描-源码分析 声明关键点 源 ...
最新文章
- Blender创建三维教室场景学习教程 3D Classroom Environment Creation in Blender
- iphone通讯录批量删除_iPhone通讯录删除了如何恢复?用对方法快速找回,亲测有效!_...
- android 删除模拟器,android – 如何从avd设备中删除脱机模拟器?
- PHP实现一个轻量级容器
- 你不知道的Event Loop
- I2C通信读写数据过程
- 医疗大数据分析需考虑哪些因素
- typescript step by step interface class
- CMM3下的应用及改进
- 【Python】爬取xici和快代理的免费代理ip
- 扫雷游戏计算机版,扫雷经典版电脑版
- eeupdate使用说明_UNRAID中文插件分享以及部分问题解决方案
- 计算机网络按覆盖地域分为,计算机网络按其所覆盖的地域范围一般可分为________ 。...
- wamp3.1.4下载及WampServer下增加多版本PHP
- 三小时学会Kubernetes:容器编排详细指南
- 为什么我们的数据还不够开放?
- 支付宝H5,微信H5,微信公众号支付回调
- mac设置文件权限_如何在Mac上设置文件权限
- FreeType字体引擎介绍
- 项目经理之新任项目经理的五项修炼
热门文章
- mysql 一次性导入数据库_Mysql 一次性备份导出/导入恢复所有数据库
- 第三方分享接口api
- nacos作注册中心+feign接口调用进行服务提供和服务消费代码示例
- 操作系统 课堂练习题01【15道 经典题目】
- Android 性能优化——绘制优化
- JAVA多线程中wait()方法的详细分析
- HDU 4323 bk树 编辑距离
- 解决mysql操作1045错误,1153错误和1130错误
- 计算机科学与技术专业导向ppt,计算机科学与技术专业导向讲座 第讲.ppt
- html长图滚动,Axure教程:长页或长图滚动效果