04、SpringBoot 源码分析 - SpringApplication启动流程四

SpringBoot 源码分析 - SpringApplication启动流程四

  • 初始化基本流程
  • SimpleApplicationEventMulticaster的multicastEvent广播事件
    • resolveDefaultEventType获取ResolvableType实例
      • ResolvableType的forInstance创建ResolvableType实例
    • 开始广播
      • AbstractApplicationEventMulticaster的getApplicationListeners获取所有支持该事件的监听器
      • DefaultListenerRetriever
        • getApplicationListeners直接获取监听器集合
      • AbstractApplicationEventMulticaster的retrieveApplicationListeners

初始化基本流程

在这里插入图片描述

SimpleApplicationEventMulticaster的multicastEvent广播事件

其实这个就是观察者模式,发布订阅模式,或者说事件驱动模式,如果看过netty就知道他里面的处理器的方法就是事件驱动模式,这里会先进行事件类型解析,解析成ResolvableType类型。
org.springframework.context.event.SimpleApplicationEventMulticaster

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

resolveDefaultEventType获取ResolvableType实例

	private ResolvableType resolveDefaultEventType(ApplicationEvent event) {return ResolvableType.forInstance(event);}

ResolvableType的forInstance创建ResolvableType实例

如果是ResolvableTypeProvider类型就会获取类型实例返回,否则会创建一个ResolvableType 对象,将事件类型封装进去。

	public static ResolvableType forInstance(@Nullable Object instance) {if (instance instanceof ResolvableTypeProvider) {ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();if (type != null) {return type;}}return (instance != null ? forClass(instance.getClass()) : NONE);}public static ResolvableType forClass(@Nullable Class<?> clazz) {return new ResolvableType(clazz);//创建一个,把clazz封装进去}

开始广播

获取所有监听器,如果没有执行器就直接调用,有的话会开线程调用,最终都是 invokeListener

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

AbstractApplicationEventMulticaster的getApplicationListeners获取所有支持该事件的监听器

现在有个启动事件,但是不一定所有监听器都要支持该事件,如果不支持表示对此事件不关心,那就不需要通知给他了,所以这里就是为了找出支持该事件的监听器集合,找出来之后还会给事件和监听器集合做映射,放入缓存中。

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);// 定义一个检索器CachedListenerRetriever newRetriever = null;// 从缓存中获取CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey); //获取监听器检索其if (existingRetriever == null) {// Caching a new ListenerRetriever if possibleif (this.beanClassLoader == null ||(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {newRetriever = new CachedListenerRetriever();existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);if (existingRetriever != null) {newRetriever = null;  // no need to populate it in retrieveApplicationListeners}}}if (existingRetriever != null) {//获取支持该事件的监听器集合,并放入retriever中Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();if (result != null) {return result;}}//不用缓存的情况,每次都取一遍return retrieveApplicationListeners(eventType, sourceType, newRetriever);}

DefaultListenerRetriever

这个其实就是存监听器的,但是只是对某些事件支持的监听器集合,可以是实例,也可以是 bean 的名字。

private class DefaultListenerRetriever {public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();public final Set<String> applicationListenerBeans = new LinkedHashSet<>();}
getApplicationListeners直接获取监听器集合

直接获取就是将bean名字的集合里的bean都实例化,然后跟监听器合并,去重,最后返回

public Collection<ApplicationListener<?>> getApplicationListeners() {List<ApplicationListener<?>> allListeners = new ArrayList<>(this.applicationListeners.size() + this.applicationListenerBeans.size());allListeners.addAll(this.applicationListeners);if (!this.applicationListenerBeans.isEmpty()) {BeanFactory beanFactory = getBeanFactory();for (String listenerBeanName : this.applicationListenerBeans) {try {ApplicationListener<?> listener =beanFactory.getBean(listenerBeanName, ApplicationListener.class);if (!allListeners.contains(listener)) {allListeners.add(listener);}}catch (NoSuchBeanDefinitionException ex) {// Singleton listener instance (without backing bean definition) disappeared -// probably in the middle of the destruction phase}}}AnnotationAwareOrderComparator.sort(allListeners);return allListeners;}

AbstractApplicationEventMulticaster的retrieveApplicationListeners

这个就是根据监听器是否支持该事件,进行监听器的筛选,最后把支持的监听器集合返回。其实还会涉及到bean名字的集合,支持该事件的会直接获取,然后放进不同的集合里,最后都会讲所有支持的监听器全部返回。这里会把监听器集合也放在ListenerRetriever里,以便于做事件类型和监听器集合的映射缓存。

private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {List<ApplicationListener<?>> allListeners = new ArrayList<>();Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);Set<ApplicationListener<?>> listeners;Set<String> listenerBeans;synchronized (this.defaultRetriever) {//获取SimpleApplicationEventMulticaster添加监听器的时候就加入进去的监听器集合listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);}// Add programmatically registered listeners, including ones coming// from ApplicationListenerDetector (singleton beans and inner beans).for (ApplicationListener<?> listener : listeners) {//遍历所有的监听器,看监听器是否支持该事件if (supportsEvent(listener, eventType, sourceType)) {if (retriever != null) {filteredListeners.add(listener);//支持就添加进去}allListeners.add(listener);//也添加到allListeners里}}// Add listeners by bean name, potentially overlapping with programmatically// registered listeners above - but here potentially with additional metadata.if (!listenerBeans.isEmpty()) {//如果有监听的bean的话,也要加进去ConfigurableBeanFactory beanFactory = getBeanFactory();for (String listenerBeanName : listenerBeans) {try {if (supportsEvent(beanFactory, listenerBeanName, eventType)) {//支持该事件的直接获取ApplicationListener<?> listener =beanFactory.getBean(listenerBeanName, ApplicationListener.class);if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {if (retriever != null) {if (beanFactory.isSingleton(listenerBeanName)) {filteredListeners.add(listener);}else {filteredListenerBeans.add(listenerBeanName);}}allListeners.add(listener);}}else {//不支持就删除// Remove non-matching listeners that originally came from// ApplicationListenerDetector, possibly ruled out by additional// BeanDefinition metadata (e.g. factory method generics) above.Object listener = beanFactory.getSingleton(listenerBeanName);if (retriever != null) {filteredListeners.remove(listener);}allListeners.remove(listener);}}catch (NoSuchBeanDefinitionException ex) {// Singleton listener instance (without backing bean definition) disappeared -// probably in the middle of the destruction phase}}}AnnotationAwareOrderComparator.sort(allListeners);if (retriever != null) {if (filteredListenerBeans.isEmpty()) {retriever.applicationListeners = new LinkedHashSet<>(allListeners);retriever.applicationListenerBeans = filteredListenerBeans;}else {retriever.applicationListeners = filteredListeners;retriever.applicationListenerBeans = filteredListenerBeans;}}return allListeners;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/12983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

脉冲水路清洗机,全自动脉冲技术清除管道堵塞

邦注脉冲水路清洗机是一种高效的清洗设备&#xff0c;它利用全自动脉冲技术来清除管道内的堵塞和污垢。以下是对该设备的一些详细描述&#xff1a; 全自动脉冲技术&#xff1a;脉冲水路清洗机采用了全自动脉冲技术&#xff0c;这是一种先进的清洗方法。该技术通过产生高强度的…

window10下安装ubuntu系统以及docker使用

window10下安装ubuntu系统以及docker使用 1. 启用适用于Linux的Windwos子系统2.下载Linux内核更新包3.将 WSL 2 设置为默认版本4.安装Ubuntu<br />直接去Microsoft store里面直接搜索Ubuntu进行安装。5.可能出现的问题1.win10启动ubuntu报错 参考的对象类型不支持尝试的操…

Linux|基础环境开发工具使用(1)

目录 Linux 软件包管理器 yum 什么是软件包 关于 rzsz 注意事项 查看软件包 如何安装软件 如何卸载软件 Linux编辑器-vim介绍 vi与vim的相同点 vi与vim区别 Linux 软件包管理器 yum 什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译…

【WebGPU】WebGPU 中的反应扩散计算着色器

在本教程中&#xff0c;我们将使用 WebGPU 技术中的计算着色器实现图像效果。更多精彩内容尽在数字孪生平台。 程序结构 主要构建两个 WebGPU 管道&#xff1a; 运行反应扩散算法多次迭代的计算管道&#xff08;js/rd-compute.js 和 js/shader/rd-compute-shader.js&#xff…

script标签以及defer和async属性

1. <script>标签 将JavaScript代码嵌入到HTML中主要方式是使用<script>元素。 使用<script>的方式有两种&#xff1a; &#xff08;1&#xff09;直接在网页中嵌入JavaScript代码&#xff1a; <script>function sayHi() {console.log("Hi"…

Leetcode—2244. 完成所有任务需要的最少轮数【中等】

2024每日刷题&#xff08;136&#xff09; Leetcode—2244. 完成所有任务需要的最少轮数 实现代码 class Solution { public:int minimumRounds(vector<int>& tasks) {unordered_map<int, int> map;for(int task: tasks) {map[task];}int ans 0;// freq 1 …

【前端】CSS基础(3)

文章目录 前言1. CSS常用元素属性1.1 字体属性1.1.1 字体1.1.2 字体大小1.1.3 字体颜色1.1.4 字体粗细1.1.5 文字样式 前言 这篇博客仅仅是对CSS的基本结构进行了一些说明&#xff0c;关于CSS的更多讲解以及HTML、Javascript部分的讲解可以关注一下下面的专栏&#xff0c;会持续…

c++父类指针指向子类

有一个常见的c题&#xff0c;就是父类和子类的构造函数和析构函数分别调用顺序&#xff1a; 父类构造函数子类构造函数子类析构函数父类析构函数 以及父类中的函数在子类中重新实现后&#xff0c;父类指针指向子类后&#xff0c;该指针调用的函数是父类中的还是子类中的&…

震撼发布!GPT-4o 上线!

5 月 14日凌晨一点&#xff0c;OpenAI 发布了 GPT-4o&#xff01; 新模型的功能简单概括就是&#xff1a;更快、更智能、更像人类。 秉承着持续更新的态度&#xff0c;Hulu AI 快速接入 GPT-4o 啦&#xff01; 继 5 月份上线 Suno 之后&#xff0c;这次是 Hulu AI 的又一重大…

vue3专栏项目 -- 六、上传组件(上)

1、上传组件需求分析 我们还需要新建和展示文章&#xff0c;新建文章自然是发送post请求&#xff0c;同时在post中自带对应的数据&#xff0c;展示文章就是根据id取出已有的数据并且展示出来。 这里有一个难点就是上传组件&#xff0c;上传文件是App应用中最基本的需求&#…

Socks5:网络世界的隐形斗篷

在数字化时代&#xff0c;网络隐私和安全已成为人们日益关注的话题。Socks5&#xff0c;作为一种代理协议&#xff0c;为用户在网络世界中的匿名性提供了强有力的支持。本文将从Socks5的多个方面&#xff0c;深入探讨这一技术如何成为网络世界的“隐形斗篷”。 一、Socks5的基本…

linux基础指令讲解(ls、pwd、cd、touch、mkdir)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 这个是我们今天要用到的初始…

P8805 [蓝桥杯 2022 国 B] 机房

P8805 [蓝桥杯 2022 国 B] 机房 分析 是一道lca题目&#xff0c;可以直接套模板 前缀和处理点权 具体思路&#xff1a; 1.n台电脑用n-1条网线相连&#xff0c;任意两个节点之间有且仅有一条路径&#xff08;拆分成各自到公共祖先节点的路径——lca&#xff09;&#xff1b;…

波搜索算法(WSA)-2024年SCI新算法-公式原理详解与性能测评 Matlab代码免费获取

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 一、初始化阶段 二、全…

我与C++的爱恋:string类的常见接口函数

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 朋友们大家好啊&#xff0c;本节我们来到STL内容的第一部分&#xff1a;string类接口函数的介绍 ​ ​ 1.string类的认识 给大家分享一个c文档 https://legacy.cplusplus.…

Weblogic 管理控制台未授权远程命令执行漏洞(CVE-2020-14882,CVE-2020-14883)

1 漏洞概述 Weblogic Pre-Auth Remote Command Execution 漏洞&#xff08;CVE-2020-14882, CVE-2020-14883&#xff09;是针对 Oracle WebLogic Server 的两个安全漏洞。CVE-2020-14882 允许远程用户绕过管理员控制台组件中的身份验证&#xff0c;而 CVE-2020-14883 则允许经…

Sam Blackshear谈Move语言的起源

Move编程语言作为Sui生态系统的关键组成部分&#xff0c;通过可编程交易区块等机制支持其独特的对象数据模型&#xff0c;并支持高效的代码。五年前&#xff0c;Mysten Labs的联合创始人兼首席技术官Sam Blackshear创建了Move。他专门设计了Move&#xff0c;用于编写智能合约&a…

sqli-labs靶场第十四关

目录 1&#xff1a;分析 找闭合符&#xff1a; 2&#xff1a;开始注入 报错注入&#xff1a; 注入数据库名&#xff1a; 注入表名&#xff1a; 注入列名&#xff1a; 注入具体值&#xff1a; 1&#xff1a;分析 经过我们的实验发现当我们输入的密码后面存在双引号时会报…

【C++】学习笔记——多态_1

文章目录 十二、继承8. 继承和组合 十三、多态1. 多态的概念2. 多态的定义和实现虚函数重写的两个特殊情况override 和 final 3. 多态的原理1. 虚函数表 未完待续 十二、继承 8. 继承和组合 我们已经知道了什么是继承&#xff0c;那组合又是什么&#xff1f;下面这种情况就是…

英语学习笔记13——A new dress

A new dress 一件新连衣裙 词汇 Vocabulary colour / color n. 颜色 v. 上色&#xff0c;涂色  英  美 颜色短语&#xff1a;green hand 新手      black tea 红茶      white house 白宫      black sheep 害群之马 英文颜色类词汇&#xff1a; red 红色…