【Spring Boot 源碼學習】ApplicationListener 詳解

Spring Boot 源碼學習系列

在這里插入圖片描述

ApplicationListener 詳解

  • 引言
  • 往期內容
  • 主要內容
    • 1. 初識 ApplicationListener
    • 2. 加載 ApplicationListener
    • 3. 響應應用程序事件
  • 總結

引言

書接前文《初識 SpringApplication》,我們從 Spring Boot 的啟動類 SpringApplication 上入手,了解了 SpringApplication 實例化過程。其中,《BootstrapRegistryInitializer 詳解》 和 《ApplicationContextInitializer 詳解》博文中,Huazie 已經帶大家詳細分析了 BootstrapRegistryInitializerApplicationContextInitializer 的加載和初始化過程,如下還有 2.5 還未詳細分析:
在這里插入圖片描述
那本篇博文就主要圍繞 2.5 的內容展開,詳細分析一下 ApplicationListener 的加載和處理應用程序事件的邏輯。

在這里插入圖片描述

往期內容

在開始本篇的內容介紹之前,我們先來看看往期的系列文章【有需要的朋友,歡迎關注系列專欄】:

Spring Boot 源碼學習
Spring Boot 項目介紹
Spring Boot 核心運行原理介紹
【Spring Boot 源碼學習】@EnableAutoConfiguration 注解
【Spring Boot 源碼學習】@SpringBootApplication 注解
【Spring Boot 源碼學習】走近 AutoConfigurationImportSelector
【Spring Boot 源碼學習】自動裝配流程源碼解析(上)
【Spring Boot 源碼學習】自動裝配流程源碼解析(下)
【Spring Boot 源碼學習】深入 FilteringSpringBootCondition
【Spring Boot 源碼學習】OnClassCondition 詳解
【Spring Boot 源碼學習】OnBeanCondition 詳解
【Spring Boot 源碼學習】OnWebApplicationCondition 詳解
【Spring Boot 源碼學習】@Conditional 條件注解
【Spring Boot 源碼學習】HttpEncodingAutoConfiguration 詳解
【Spring Boot 源碼學習】RedisAutoConfiguration 詳解
【Spring Boot 源碼學習】JedisConnectionConfiguration 詳解
【Spring Boot 源碼學習】初識 SpringApplication
【Spring Boot 源碼學習】Banner 信息打印流程
【Spring Boot 源碼學習】自定義 Banner 信息打印
【Spring Boot 源碼學習】BootstrapRegistryInitializer 詳解
【Spring Boot 源碼學習】ApplicationContextInitializer 詳解

主要內容

注意: 以下涉及 Spring Boot 源碼 均來自版本 2.7.9,其他版本有所出入,可自行查看源碼。

1. 初識 ApplicationListener

我們先來看看 ApplicationListener 接口的源碼【spring-context-5.3.25.jar】:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {return event -> consumer.accept(event.getPayload());}
}

從上述代碼,我們可以看到 ApplicationListener 接口被 @FunctionalInterface 注解修飾。

知識點: @FunctionalInterfaceJava 8 中引入的一個注解,用于標識一個函數式接口。函數式接口是只有一個抽象方法的接口,常用于實現 Lambda 表達式和方法引用。
使用 @FunctionalInterface 注解可以向編譯器指示該接口是一個函數式接口,從而在編譯時進行類型檢查,確保該接口 只包含一個抽象方法。此外,該注解還可以為函數式接口生成特殊的方法,如默認方法(default method)和 靜態方法(static method),這些方法可以在接口中提供更多的功能,這里就不贅述了,感興趣的朋友可以自行查閱相關函數式接口的資料。

ApplicationListenerSpring 中應用程序事件監聽器實現的接口。它基于觀察者設計模式的java.util.EventListener 接口的標準。在注冊到 Spring ApplicationContext 時,事件將進行相應的過濾,只有匹配的事件對象才會使該監聽器被調用。

ApplicationListener 接口中,我們可以看到它定義了一個 onApplicationEvent(E event) 方法,當監聽事件被觸發時,onApplicationEvent 方法就會被調用執行。onApplicationEvent 方法一般用于處理應用程序事件,參數 eventApplicationEvent 的子類,也就是具體要響應處理的各種類型的應用程序事件。例如,當某個特定事件發生時,你可能想要記錄日志、更新數據庫、發送電子郵件等等。

另外,ApplicationListener 接口還提供了一個靜態方法 forPayload(Consumer<T> consumer),用于創建一個新的 ApplicationListener 實例。這個方法接受一個 Consumer<T> 類型的參數,這個參數是一個函數接口,它接受一個泛型參數 T,并對其執行一些操作。通過這個方法,你可以將一個 Consumer 函數作為參數,然后返回一個對應的事件監聽器。這個監聽器會在事件發生時,調用 Consumer 函數處理事件的有效載荷【即事件中包含的有效信息或數據】。

2. 加載 ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

上述代碼是 SpringApplication 的核心構造方法中的邏輯,它用于加載實現了 ApplicationListener 接口的監聽器實例集合,并將該監聽器實例集合設置到 SpringApplicationlisteners 變量中。

private List<ApplicationContextInitializer<?>> initializers;

我們進入 getSpringFactoriesInstances 方法,查看如下:

在這里插入圖片描述

我們看到了如下的代碼 :

SpringFactoriesLoader.loadFactoryNames(type, classLoader);

這里是通過 SpringFactoriesLoader 類的 loadFactoryNames 方法來獲取 META-INF/spring.factories 中配置 key 為 org.springframework.context.ApplicationListener 的數據;

我們以 spring-boot-autoconfigure-2.7.9.jar 為例:

在這里插入圖片描述

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

3. 響應應用程序事件

這里我們需要查看 SpringApplicationrun(String... args) 方法,如下所示:

在這里插入圖片描述

我們看上面的 SpringApplicationRunListeners ,其內的 listeners 變量是 SpringApplicationRunListener 接口的集合,如下所示:

在這里插入圖片描述

SpringApplicationRunListener 接口的一個實現就是 EventPublishingRunListener 類,該類的作用就是根據 Spring Boot 程序啟動過程的 不同階段 發布對應的事件,然后由不同的實現 ApplicationListener 接口的應用程序監聽器,來處理對應的事件【有關 SpringApplicationRunListener 監聽器的內容,我們后續博文中會詳細介紹,這里不展開了】。

如下圖是 SpringApplicationRunListeners 類中的方法,它們分別對應了 Spring Boot 程序啟動過程中要發布的不同階段的事件的邏輯。

在這里插入圖片描述

  • starting :當 run 方法第一次被執行時,該方法會立即被調用,可用于非常早期的初始化工作
  • environmentPrepared :當 environment 準備完成,在 ApplicationContext 創建之前,該方法被調用
  • contextPrepared :當 ApplicationContext 構建完成,資源還未被加載時,該方法被調用
  • contextLoaded :當 ApplicationContext 加載完成,未被刷新之前,該方法被調用
  • started :當 ApplicationContext 刷新并啟動之后,CommandLineRunnerApplicationRunner 未被調用之前,該方法被調用
  • ready :當所有準備工作就緒,run 方法執行完成之前,該方法被調用
  • failed :當應用程序出現錯誤時,該方法被調用

我們以 starting 方法的邏輯為例,看一下 ApplicationStartingEvent 事件發布并被處理的過程。

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});
}

我們繼續看 doWithListeners 方法:

在這里插入圖片描述

結合上面的截圖,我們重點看下這行:

(listener) -> listener.starting(bootstrapContext)

這里時調用了 SpringApplicationRunListener 接口的 starting 方法:

在這里插入圖片描述

這里的 multicastEvent 方法就是用來發布一個指定的應用程序事件,比如這里發布的就是 ApplicationStartingEvent 事件。

在這里插入圖片描述
在這里插入圖片描述

總結

本篇 Huazie 帶大家詳細分析了 ApplicationListener 的加載和處理應用程序事件,這對于后續的 SpringApplication 運行流程的理解至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212276.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212276.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212276.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何查詢川菜食材配料的API接口

在當今的美食文化中&#xff0c;菜譜不只是一張簡單的食譜&#xff0c;更是了解美食文化和飲食知識的重要途徑。然而&#xff0c;若沒有準確的食材配料&#xff0c;烹制出的每道菜品都將難以達到完美的味道。因此&#xff0c;為了更好地滿足人們對于菜譜和食譜的需求&#xff0…

C語言習題集(026)

//寫一個函數&#xff0c;輸入一個4位數字&#xff0c;要求輸出這4個 //數字字符&#xff0c;但每兩個數字間空一個空格。如輸入 //1990&#xff0c;應輸出"1 9 9 0"。 /* */ //解答&#xff1a; #include<stdio.h> void change(int a) { if(a/10!0) { chang…

linux權限管理以及shell

1.shell 1.1什么是shell? shell即外殼&#xff0c;是運行在linux系統上的一個腳本語言&#xff0c;包裹在linux內核的外面。我們常說的linux操作系統實際上是linux內核。我們使用的所有指令都是一個個程序&#xff0c;而shell指令就是一個將我們用戶的操作翻譯給linux內核的程…

軟件設計之組合模式

組合模式&#xff1a;將對象組合成樹形結構。 案例&#xff1a;公司管理。一個公司可以分總公司和分公司&#xff0c;無論是總公司還是分公司都有自己的部門&#xff0c;如人力資源管理部門、財務部門。分公司可以建立自己在不同地域的辦事處。請使用組合模式打印出某個公司的…

SpringSecurity6 | 登陸后的跳轉

SpringSecurity6 | 自定義認證規則 ?作者簡介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;熱愛Java后端開發者&#xff0c;一個想要與大家共同進步的男人&#x1f609;&#x1f609; &#x1f34e;個人主頁&#xff1a;Leo的博客 &#x1f49e;當前專欄&#xff1a; Ja…

第九天:信息打點-CDN繞過篇amp;漏洞回鏈amp;接口探針amp;全網掃描amp;反向郵件

信息打點-CDN繞過篇 cdn繞過文章&#xff1a;https://www.cnblogs.com/qiudabai/p/9763739.html 一、CDN-知識點 1、常見訪問過程 1、沒有CDN情況下傳統訪問&#xff1a;用戶訪問域名-解析服務器IP–>訪問目標主機 2.普通CDN&#xff1a;用戶訪問域名–>CDN節點–>…

面向LLM的App架構——業務維度

這是兩篇面向LLM的大前端架構的第一篇&#xff0c;主要寫我對LLM業務的認知以及由此推演出的大前端架構。由于我是客戶端出身&#xff0c;所以主要以客戶端角度來描述&#xff0c;并不影響對前端的適用性。 對LLM的認知 基于Google對AGI的論文&#xff0c;AGI或者LLM一定會朝…

淺談ClickHouse性能監控與調優

ClickHouse性能監控與調優 ClickHouse是一個高性能的列式數據庫管理系統&#xff0c;適用于實時分析和大數據處理。本文將詳細講解如何監控ClickHouse的性能指標、日志和查詢統計信息&#xff0c;以及如何進行故障排查和性能調優。 一、監控性能指標 1. 系統表 ClickHouse提…

網絡層重點協議——IP協議詳解

??????今天給大家分享的是網絡層的重點協議——IP協議。 清風的CSDN博客 &#x1f6e9;?&#x1f6e9;?&#x1f6e9;?希望我的文章能對你有所幫助&#xff0c;有不足的地方還請各位看官多多指教&#xff0c;大家一起學習交流&#xff01; ??????動動你們發財的…

阿里內部教程Jmeter 性能測試常用圖表、服務器資源監控

性能測試常用圖表 插件安裝 步驟 1&#xff1a;安裝插件管理器 在 Jmeter 官網上下載插件管理器 Plugins-manager-1.3.jar將 jar 包放入到 lib\ext 目錄下重啟 Jmeter&#xff0c;可以在選項下看到 Plugins Manager 選項 步驟 2&#xff1a;安裝指定的插件 打開 Plugins Ma…

JVM虛擬機系統性學習-運行時數據區(堆)

運行時數據區 JVM 由三部分組成&#xff1a;類加載系統、運行時數據區、執行引擎 下邊講一下運行時數據區中的構成 根據線程的使用情況分為兩類&#xff1a; 線程獨享&#xff08;此區域不需要垃圾回收&#xff09; 虛擬機棧、本地方法棧、程序計數器 線程共享&#xff08;數…

【矩陣】73. 矩陣置零

題目 法1&#xff1a;自己想的笨蛋方法 class Solution {public void setZeroes(int[][] matrix) {Set<Integer> rowSet new HashSet<>();Set<Integer> columnSet new HashSet<>();for (int i 0; i < matrix.length; i) {for (int j 0; j <…

DataGrip常見問題

查詢語句結果沒有輸出在output中 進行如下配置 配置后查詢結果輸出在output中 左側數據庫鏈接信息導航欄被隱藏 以上導航欄被隱藏&#xff0c;按下圖操作調出

【Qt開發流程】之容器類2:使用STL風格迭代器進行遍歷

概述 對于每個容器類&#xff0c;都有兩種stl風格的迭代器類型:一種提供只讀訪問&#xff0c;另一種提供讀寫訪問。應該盡可能使用只讀迭代器&#xff0c;因為它們比讀寫迭代器快。 STL迭代器的API以數組中的指針為模型。例如&#xff0c;操作符將迭代器推進到下一項&#xf…

Java開發工具:IDEA 2023.3(WinMac)中文激活版

IntelliJ IDEA 2023是一款由JetBrains公司出品的集成開發環境&#xff08;IDE&#xff09;&#xff0c;專為程序員設計。它以智能、高效和人性化為主要特點&#xff0c;致力于提高開發人員的生產力&#xff0c;幫助程序員更快、更好地編寫代碼。 在智能功能方面&#xff0c;Int…

Panalog 日志審計系統 sprog_deletevent.php SQL 注入漏洞復現

0x01 產品簡介 Panalog大數據日志審計系統定位于將大數據產品應用于高校、 公安、 政企、 醫療、 金融、 能源等行業之中&#xff0c;針對網絡流量的信息進行日志留存&#xff0c;可對用戶上網行為進行審計&#xff0c;逐漸形成大數據采集、 大數據分析、 大數據整合的工作模式…

c語言一維數組總結詳解

目錄 介紹&#xff1a; 一維整型數組&#xff1a; 聲明&#xff1a; 初始化&#xff1a; 打印輸出&#xff1a; 輸出結果&#xff1a; 浮點型數組&#xff1a; 代碼&#xff1a; 運行結果&#xff1a; 補充&#xff1a; 一維字符數組&#xff1a; 字符數組聲明及初始…

Python軸承故障診斷 (二)連續小波變換CWT

目錄 前言 1 連續小波變換CWT原理介紹 1.1 CWT概述 1.2 CWT的原理和本質 2 基于Python的CWT實現與參數對比 2.1 代碼示例 2.2 參數介紹和選擇策略 2.2.1 尺度長度&#xff1a; 2.2.2 小波函數&#xff08;wavelet&#xff09;&#xff1a; 2.3 凱斯西儲大學軸承數據的…

《算法與數據結構》答疑

答疑 問題一問題二問題三問題四 問題一 在匹配成功時&#xff0c;在返回子串位置那里&#xff0c;為什么不是i-t的長度啊&#xff0c;為什么還要加一 問題二 問題三 問題四 問&#xff1a;如果題目讓我們構造一個哈夫曼樹&#xff0c;像我發的這個例題的話&#xff0c;我畫成我…

深度學習與計算機視覺技術的融合

深度學習與計算機視覺技術的融合 一、引言 隨著人工智能技術的不斷發展&#xff0c;深度學習已經成為了計算機視覺領域的重要支柱。計算機視覺技術能夠從圖像和視頻中提取有用的信息&#xff0c;而深度學習則能夠通過學習大量的數據來提高計算機視覺技術的性能。本文將探討深…