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.pswp.cn/web/12983.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12983.shtml
英文地址,請注明出處:http://en.pswp.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…

react 渲染引擎經歷了那些迭代

React 渲染引擎經歷了多個迭代&#xff0c;主要集中在改進 Virtual DOM 的實現和優化渲染性能方面。以下是一些 React 渲染引擎的主要迭代&#xff1a; React Fiber 架構&#xff1a;React 16 引入了 Fiber 架構&#xff0c;這是一個重新實現的渲染引夠更好地支持異步渲染。 S…

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;…

Delphi7:SuperObject 的示例

SuperObject 不是一個 Delphi 7 自帶或官方的庫&#xff0c;但可能是指一些開源的 JSON 解析庫&#xff0c;比如 superobject 或 dwscript 中的 SuperObject。這些庫通常用于解析和生成 JSON 數據。 以下是一個基于假設的 SuperObject 用法概述&#xff0c;因為不同的庫可能有…

波搜索算法(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 則允許經…

Python3 筆記:Python的函數

函數是編寫好的&#xff0c;可重復調用的&#xff0c;用來實現某一功能的一段代碼。 也可以理解為做某事的方法和步驟。第一次做的時候寫好了方法&#xff0c;下次同類型的事直接拿來就用。 Python 內部有很多功能強大的內置函數供我們使用&#xff0c;除此之外&#xff0c;你…

Nagle算法

Nagle算法簡介 Nagle算法主要是避免發送小的數據包&#xff0c;要求TCP連接上最多只能有一個未被確認的小分組&#xff0c;在該分組的確認到達之前不能發送其他的小分組。 在默認的情況下,Nagle算法是默認開啟的&#xff0c;Nagle算法比較適用于發送方發送大批量的小數據&…