SpringBoot深入分析讲解监听器模式下


Posted in Java/Android onJuly 15, 2022

我们来以应用启动事件:ApplicationStartingEvent为例来进行说明:

以启动类的SpringApplication.run方法为入口,跟进SpringApplication的两个同名方法后,我们会看到主要的run方法,方法比较长,在这里只贴出与监听器密切相关的关键的部分:

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

我们跟进这个starting方法,方法的内容如下:

void starting() {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.starting();
	}
}

这里的listeners已经在getRunListeners方法中完成了加载,加载原理类似于系统初始化器

starting方法逻辑很简单,就是调用SpringApplicationRunListener的starting方法。下面继续分析这个starting方法:

我们进入了EventPublishingRunListener类(SpringApplicationRunListener 的实现类)的starting方法:

@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

这里就使用了广播器,来广播新的ApplicationStartingEvent事件。

我们跟进这个multicastEvent方法:

@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

继续看同名的方法multicastEvent:

@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);
			}
		}
	}

这里的ResolvableType 是对event做了包装,我们不去关注;由于我们没有创建线程池,所以executor是空的。我们重点关注两个部分:

1、getApplicationListeners --> 获取所有关注此事件的监听器(※);

2、invokeListener --> 激活监听器;

getApplicationListeners (AbstractApplicationEventMulticaster类中)方法,代码如下:

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {
		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
		// Quick check for existing entry on ConcurrentHashMap...
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}
		if (this.beanClassLoader == null ||
				(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
			// Fully synchronized building and caching of a ListenerRetriever
			synchronized (this.retrievalMutex) {
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				Collection<ApplicationListener<?>> listeners =
						retrieveApplicationListeners(eventType, sourceType, retriever);
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		}
		else {
			// No ListenerRetriever caching -> no synchronization necessary
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}

入参中的event就是ApplicationStartingEvent,sourceType是org.springframework.boot.SpringApplication类。ListenerRetriever类型本人将其视作是一个保存监听器的容器。

可以看出,程序首先在缓存里面寻找ListenerRetriever类型的retriever,如果没有找到,加锁再从缓存里面找一次。这里我们缓存里是没有内容的,所以都不会返回。

接下来调用了retrieveApplicationListeners方法,来遍历所有的监听器。retrieveApplicationListeners方法比较长,我们重点关注下supportsEvent(listener, eventType, sourceType)方法,该方法用来判断是否此监听器关注该事件,过程主要包括,判断此类型是否是GenericApplicationListener类型,如果不是,则构造一个代理,代理的目的是,通过泛型解析,最终获得监听器所感兴趣的事件。

如果经过判断,监听器对该事件是感兴趣的,则此监听器会被加入监听器列表中。

protected boolean supportsEvent(
			ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
		GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
				(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
		return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
	}

当某个事件所有的监听器被收集完毕后,multicastEvent(SimpleApplicationEventMulticaster类)方法会对事件进行传播。即调用监听器的通用触发接口方法:listener.onApplicationEvent(event);这样,就完成了这个事件的传播。

到此这篇关于SpringBoot深入分析讲解监听器模式下的文章就介绍到这了,更多相关SpringBoot监听器模式内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
Java Shutdown Hook场景使用及源码分析
Jun 15 Java/Android
Java如何实现树的同构?
Jun 22 Java/Android
eclipse创建项目没有dynamic web的解决方法
Jun 24 Java/Android
springboot拦截器无法注入redisTemplate的解决方法
Jun 27 Java/Android
HashMap实现保存两个key相同的数据
Jun 30 Java/Android
浅谈resultMap的用法及关联结果集映射
Jun 30 Java/Android
使用HttpSessionListener监听器实战
Mar 17 Java/Android
springboot 自定义配置 解决Boolean属性不生效
Mar 18 Java/Android
Java8 CompletableFuture 异步回调
Apr 28 Java/Android
Android Studio实现简易进制转换计算器
May 20 Java/Android
Android Canvas绘制文字横纵向对齐
Jun 05 Java/Android
httpclient调用远程接口的方法
Aug 14 Java/Android
tree shaking对打包体积优化及作用
Jul 07 #Java/Android
MyBatis在注解上使用动态SQL方式(@select使用if)
Jul 07 #Java/Android
一文了解Java动态代理的原理及实现
Jul 07 #Java/Android
Java实现字符串转为驼峰格式的方法详解
Jul 07 #Java/Android
Spring中bean集合注入的方法详解
java中如何截取字符串最后一位
Jul 07 #Java/Android
Java 中的 Lambda List 转 Map 的多种方法详解
Jul 07 #Java/Android
You might like
PHP新手上路(十)
2006/10/09 PHP
Laravel中使用Queue的最基本操作教程
2017/12/27 PHP
HTML5之lang属性与dir属性的详解
2013/06/19 Javascript
js截取中英文字符串、标点符号无乱码示例解读
2014/04/17 Javascript
JavaScript设计模式之装饰者模式介绍
2014/12/28 Javascript
JavaScript通过join函数连接数组里所有元素的方法
2015/03/20 Javascript
JavaScript中var关键字的使用详解
2015/08/14 Javascript
AngularJS实现标签页的两种方式
2016/09/05 Javascript
微信小程序 Button 组件详解及简单实例
2017/01/10 Javascript
touch.js 拖动、缩放、旋转 (鼠标手势)功能代码
2017/02/04 Javascript
解决bootstrap下拉菜单点击立即隐藏bug的方法
2017/06/13 Javascript
jQuery Form插件使用详解_动力节点Java学院整理
2017/07/17 jQuery
js装饰设计模式学习心得
2018/02/17 Javascript
JavaScript交换变量的常用方法小结【4种方法】
2020/05/07 Javascript
jQuery插件实现图片轮播效果
2020/10/19 jQuery
[00:52]黑暗之门更新 新英雄孽主驾临DOTA2
2016/08/24 DOTA
[40:19]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第二场 12.18
2020/12/19 DOTA
Python使用自带的ConfigParser模块读写ini配置文件
2016/06/26 Python
python dict 字典 以及 赋值 引用的一些实例(详解)
2017/01/20 Python
Python基于回溯法子集树模板实现8皇后问题
2017/09/01 Python
Scrapy框架CrawlSpiders的介绍以及使用详解
2017/11/29 Python
Python实现矩阵加法和乘法的方法分析
2017/12/19 Python
Tensorflow中的placeholder和feed_dict的使用
2018/07/09 Python
python复制列表时[:]和[::]之间有什么区别
2018/10/16 Python
[原创]Python入门教程2. 字符串基本操作【运算、格式化输出、常用函数】
2018/10/29 Python
python实现简单颜色识别程序
2020/02/19 Python
Django用数据库表反向生成models类知识点详解
2020/03/25 Python
Python基于gevent实现高并发代码实例
2020/05/15 Python
python 利用百度API识别图片文字(多线程版)
2020/12/14 Python
口腔医学技术应届生求职信
2013/11/09 职场文书
优秀护士获奖感言
2014/02/20 职场文书
夏季药店促销方案
2014/08/22 职场文书
公司领导班子群众路线四风问题对照检查材料
2014/10/02 职场文书
投标承诺函格式
2015/01/21 职场文书
sql通过日期判断年龄函数的示例代码
2021/07/16 SQL Server
Python集合的基础操作
2021/11/01 Python