專題五:Spring源碼之初始化容器上下文

上一篇我們通過如下一段基礎代碼作為切入點,最終找到核心的處理是refresh方法,從今天開始正式進入refresh方法的解讀。

public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");JmUser jmUser = (JmUser)context.getBean("jmUser");System.out.println(jmUser.getName());System.out.println(jmUser.getAge());}
}

初始化容器上下文

首先還是整體看下refresh方法

	@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing. 1、初始化上下文信息,替換占位符、必要參數的校驗prepareRefresh();// Tell the subclass to refresh the internal bean factory. 2、解析類Xml、初始化BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 這一步主要是對初級容器的基礎設計// Prepare the bean factory for use in this context. 	3、準備BeanFactory內容:prepareBeanFactory(beanFactory); // 對beanFactory容器的功能的擴展:try {// Allows post-processing of the bean factory in context subclasses. 4、擴展點加一:空實現,主要用于處理特殊Bean的后置處理器postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context. 	5、spring bean容器的后置處理器invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation. 	6、注冊bean的后置處理器registerBeanPostProcessors(beanFactory);// Initialize message source for this context.	7、初始化消息源initMessageSource();// Initialize event multicaster for this context.	8、初始化事件廣播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses. 9、擴展點加一:空實現;主要是在實例化之前做些bean初始化擴展onRefresh();// Check for listener beans and register them.	10、初始化監聽器registerListeners();// Instantiate all remaining (non-lazy-init) singletons.	11、實例化:非蘭加載BeanfinishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.	 12、發布相應的事件通知finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}

首先將目標聚焦在第一個方法prepareRefresh方法上,根據方法名稱和注釋,我們大概可以猜測到該方法是在容器初始化前做些準備工作。

有了這個想法我來具體看下這個方法到底干了什么?

/*** Prepare this context for refreshing, setting its startup date and* active flag as well as performing any initialization of property sources.* 一些初始化設置如:設置容器開始事件、容器狀態active設置激活】初始化配置源等。* 1.1、其中關注初始化配置源:這個也是留給子類自己實現,擴展點加一* 1.2、容器初始化的時候,校驗必須的配置是否為空,當我們自己對原框架修改的時候,可以通過這個屬性加上必要的配置判斷**/protected void prepareRefresh() {// Switch to active.this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);}else {logger.debug("Refreshing " + getDisplayName());}}// Initialize any placeholder property sources in the context environment.// 初始化替換占位符為實際值initPropertySources();// Validate that all properties marked as required are resolvable:// see ConfigurablePropertyResolver#setRequiredProperties// 容器初始化的時候,校驗必須的配置是否為空getEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners...if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);}else {// Reset local application listeners to pre-refresh state.this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...this.earlyApplicationEvents = new LinkedHashSet<>();}

可以看到,該方法大部分時間只是做了初始化的設置如開始時間、容器狀態初始化等,聚焦下

initPropertySources方法
	/*** <p>Replace any stub property sources with actual instances.* @see org.springframework.core.env.PropertySource.StubPropertySource* @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources*/protected void initPropertySources() {// For subclasses: do nothing by default.}

Spring最經典的設計之一,空實現方法方法的權限級別為protected。給子類自己實現,擴展點加一。這里單獨提出來和大家看看,因為后面我們能看到很多類似的代碼。這也是Spring是一個易擴展框架的原因之一。

說完這個方法的設計,下面再來看看這個方法具體干了什么。看注釋說是為了替換占位符。既然這樣我們自己來重寫這個方法試試看就知道啦。重寫代碼如下:

public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {/*** Create a new ClassPathXmlApplicationContext, loading the definitions* from the given XML file and automatically refreshing the context.* @param configLocations resource location* @throws BeansException if context creation failed*/public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {super(configLocations);}@Overrideprotected void initPropertySources() {System.out.println("自定義 initPropertySources");getEnvironment().getSystemProperties().put("systemOS", "mac");}public class Main {public static void main(String[] args) {ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");JmUser jmUser = (JmUser)context.getBean("jmUser");System.out.println(jmUser.getName());System.out.println(jmUser.getAge());}
}

執行完initPropertySources方法以后,發現環境變量多了我們設置的代碼systemOS,后續在需要的地方可以替換成我們所需要的值。

接著我們來看prepareRefresh下一個方法:

getEnvironment().validateRequiredProperties();

老樣子根據注釋和方法名稱簡答猜測一下,應該是用來校驗是否需要檢驗某個必須的屬性。猜測后進入代碼驗證一波。

看代碼是自己的成員屬性propertyResolver進行調用的,在進入方法看下:

@Overridepublic void validateRequiredProperties() {MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();for (String key : this.requiredProperties) {if (this.getProperty(key) == null) {ex.addMissingRequiredProperty(key);}}if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}}

上述代碼主要是遍歷requirePrpperties屬性,將不存在的key存入ex中,待循環結束以后拋出異常。目前我們的代碼屬性為空。我們再改寫下上述代碼看看。

package org.springframework;import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author Jeremy* @version 1.0* @description: 自定義容器* @date 2024/7/1 20:17*/
public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {/*** Create a new ClassPathXmlApplicationContext, loading the definitions* from the given XML file and automatically refreshing the context.* @param configLocations resource location* @throws BeansException if context creation failed*/public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {super(configLocations);}@Overrideprotected void initPropertySources() {System.out.println("自定義 initPropertySources");
//		getEnvironment().getSystemProperties().put("systemOS", "mac");getEnvironment().setRequiredProperties("systemOS");}
}

堆棧日志如下

自定義 initPropertySources
Disconnected from the target VM, address: 'localhost:50403', transport: 'socket'
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [systemOS]
?? ?at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:145)
?? ?at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:519)
?? ?at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:602)
?? ?at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
?? ?at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:148)
?? ?at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:95)
?? ?at org.springframework.MyselfClassPathXmlApplicationContext.<init>(MyselfClassPathXmlApplicationContext.java:20)
?? ?at org.springframework.Main.main(Main.java:15)

放開上面注釋:正常運行。通過上述兩個簡單的實例我們可以通過重寫上述代碼為我們Spring容器提供基礎的校驗和設置對應的值。方便后續開發。到這里我們初始化容器上下文prepareRefresh方法告一段落。

總結

目前我們代碼進程如下圖所示:

下一節我們正式進入初始化容器,看看眾所周知的Bean Factory到底怎么來的。

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

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

相關文章

鴻蒙本地簽名不匹配問題

連接鴻蒙手機運行項目報如下錯誤 這是由于本地簽名和鴻蒙設備簽名不匹配導致的&#xff0c;需要注釋掉如下代碼&#xff0c;選擇file project 自動簽名 勾選auto選項&#xff0c;會在build-profile.json5中生成一個簽名&#xff0c;然后運行就ok了~

【Lua】腳本入門

文章目錄 總述一、Lua概述二、Lua環境安裝三、Lua基本語法四、Lua的庫和擴展五、Lua的應用場景六、學習資源 語法1. Lua基本語法示例變量和數據類型控制結構函數 2. Lua標準庫示例字符串操作數學函數文件I/O 3. Lua作為腳本擴展示例&#xff08;假設Lua嵌入在某個應用程序中&am…

vscode python格式化

插件 Black Formatter Black 默認會遵循 PEP 8 的規范&#xff0c;可配置的參數很少&#xff0c;用的人很多。 setting.json 配置&#xff0c;更改插件的每行字符數限制 {"[python]": {"editor.defaultFormatter": "ms-python.black-formatter"…

Redis命令大全(基礎版)

一、基礎命令 redis-server --service-start # 開啟服務 redis-server --service-stop # 停止服務redis-cli # 進入redis界面redis界面操作&#xff1a; ping # 檢測狀態&#xff0c;返回pong證明連接正常set key value # 設置 key 字段的值為value&#xff0c;返回o…

創建一個Django用戶認證系統

目錄 1、Django2、Django用戶認證系統User 模型&#xff1a;Authentication 視圖&#xff1a;認證后端 (Authentication Backends)&#xff1a;Form 類&#xff1a;中間件 (Middleware)&#xff1a;權限和組 (Permissions and Groups)&#xff1a; 3、創建一個django用戶認證系…

服務器的分類,主流服務器的應用場景

一、服務器分類 服務器可以按應用層次、體系架構、用途、外形等進行分類。以下是詳細說明&#xff1a; 按應用層次分類 入門級服務器&#xff1a;這些服務器一般用于小型企業或部門的簡單任務&#xff0c;如文件共享和打印服務。工作組級服務器&#xff1a;適用于中小型企業&…

html2canvas相關(生成圖片)

根據 DOM 生成對應的圖片 function export3png(row, type null) { html2canvas( document.querySelector(#bug), //要生成圖片的dom節點 {useCORS: true, }) 圖片跨域 .then((canvas) > { const saveUrl canvas.toDataURL(image/png) Canvas對象生成base64代碼 co…

MNIST手寫字體識別(算法基礎)

快教程 10分鐘入門神經網絡 PyTorch 手寫數字識別 慢教程 【深度學習Pytorch入門】 簡單回歸問題-1 梯度下降算法 梯度下降算法 l o s s x 2 ? s i n ( x ) loss x^2 * sin(x) lossx2?sin(x) 求導得&#xff1a; f ‘ ( x ) 2 x s i n x x 2 c o s x f^(x)2xsinx x^…

uORF和non-overlap對翻譯效率的影響

以下是重疊和非重疊上游開放閱讀框&#xff08;uORFs&#xff09;對翻譯效率影響的總結&#xff1a; 重疊uORFs&#xff1a; 重疊uORFs對主要編碼區的翻譯影響更為顯著&#xff0c;因為它們直接與下游編碼序列&#xff08;CDSs&#xff09;競爭核糖體結合。重疊uORFs的翻譯起始…

在C++程序中嵌入quickjs實現C++和javascript互相調用

quickjs是一個C實現的輕量級javascript解析引擎&#xff0c;可以嵌入到C程序中&#xff0c;實現C和js代碼的交互。 以下基于quickjs-ng這一社區分支實現樣例代碼演示利用quickjs編寫程序進行C和js互相調用&#xff0c;支持linux和windows。 代碼結構 quickjs_demo- quickjs-…

Cesium大屏-vue3注冊全局組件

1.需求 說明&#xff1a;產品經理要求開發人員在地圖大屏上面隨意放置組件&#xff0c;并且需要通過數據庫更改其組件大小&#xff0c;位置等&#xff1b;適用于大屏組件中場站視角、任意位置標題等。 2.實現 2.1GlobalComponents.vue 說明&#xff1a;containerList可以通…

python基礎語法 004-2流程控制- for遍歷

1 遍歷 1.1 什么是遍歷&#xff1f; 可以遍歷的元素&#xff1a;字符串、列表、元組、字典、集合字符串是可以進行for 循環。&#xff08;容器對象&#xff0c;序列&#xff09;可迭代對象iterable 例子&#xff1a; 1 &#xff09;、for遍歷字符串&#xff1a; name xiao…

RK3568驅動指南|第十五篇 I2C-第167章 I2C上拉電阻

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工藝&#xff0c;搭載一顆四核Cortex-A55處理器和Mali G52 2EE 圖形處理器。RK3568 支持4K 解碼和 1080P 編碼&#xff0c;支持SATA/PCIE/USB3.0 外圍接口。RK3568內置獨立NPU&#xff0c;可用于輕量級人工…

SpringBoot實現圖片添加水印

提示&#xff1a;今日完成圖片添加水印功能 后續可能還會繼續完善這個功能 文章目錄 目錄 文章目錄 前端部分 后端 Xml Controller層 Sercive層 Service實現層 Config配置層 application.properties 文件后綴名獲取 常量定義 前端部分 <!DOCTYPE html> <htm…

WIN11,如何同時連接有線網絡與WLAN無線網絡

之前寫了兩篇文章&#xff0c;一篇是雙網卡多網卡時win11如何設置網卡優先級_多網卡設置網卡優先級-CSDN博客 另一篇是win11 以太網和WLAN沖突 連接網線時導致WiFi掉線 解決_win11 以太網和wifi不能同時生效-CSDN博客 這篇是對上面兩篇的補充&#xff1a;主要解決電腦重啟后&…

語音芯片TD5580,USB小音響芯片—拓達半導體

有時候電腦的聲卡會出現損壞的問題&#xff0c;給我們的生活帶來了很多麻煩。這時候&#xff0c;我們就需要一款方便易用的產品來解決聲卡問題。USB聲卡小音響就是為了解決這個問題而設計的一款便捷的產品。它不僅可以作為一個小音響&#xff0c;讓您在工作和娛樂的時候享受高品…

docker-compose搭建minio對象存儲服務器

docker-compose搭建minio對象存儲服務器 最近想使用oss對象存儲進行用戶圖片上傳的管理&#xff0c;了解了一下例如aliyun或者騰訊云的oss對象存儲服務&#xff0c;但是呢涉及到對象存儲以及經費有限的緣故&#xff0c;決定自己手動搭建一個oss對象存儲服務器&#xff1b; 首先…

燒結銀到底有多牛?歡迎咨詢SHAREX善仁新材研究院

燒結銀到底有多牛&#xff1f;歡迎咨詢SHAREX善仁新材研究院 在當今日新月異的科技浪潮中&#xff0c;材料科學以其獨特的魅力引領著人類探索未知領域的步伐。在眾多前沿材料中&#xff0c;燒結銀憑借其卓越的性能和廣泛的應用前景&#xff0c;逐漸嶄露頭角&#xff0c;成為科…

創建XCOM窗體和跳轉連接

Xcom 窗體&#xff1a; (groupBox組合框&#xff0c;comboBox下拉框) xcom代碼&#xff1a; namespace _01_作業 {// 1kb 1024B 1200B// 1MB public partial class Form1 : Form{public List<string> botelv new List<string> { "600","1200&…

Unix Network Programming Episode 96

‘socketpair’ Function The socketpair function creates two sockets that are then connected together. This function applies only to Unix domain sockets. #include <sys/socket.h> int socketpair(int family, int type, int protocol, int sockfd[2]);POSIX…