Spring源碼十九:Bean實例化流程二

上一篇我們在Spring源碼十八:Bean實例化流程一?中,主要討論了Spring在實例化前的兩重要準備工作,1、獲取我們前面注冊好的BeanDefinition,將GenericBeanDefinition封裝為RootBeanDefinition如果Bean Definition只存在父容器中,還會進行合并操作,然后做了嚴謹的異常判斷處理。2、如果bean配置了依賴的bean的名稱,還會檢查下配置的依賴,是否已經處于bean依賴的引用鏈上了,如果沒有處于bean依賴引用鏈上,就會提前來實例化bean依賴的那些bean。最后找到實例化的入口。

?今天我們開始分析下單例bean是如何創建:咱們接著上一篇代碼往下看

getSingleton

再進入代碼中看下邏輯:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {// 從單例緩存中獲取Bean的實例Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {// 如果當前bean正在銷毀標志為true,則拋出異常。 默認:singletonsCurrentlyInDestruction = falseif (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 將當前beanName放入singletonsCurrentlyInCreation列表中,標志當前bean正在被創建beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// !!!!  調用簡單工廠方法來實例化bean  !!!! //singletonObject = singletonFactory.getObject();// 標志當前bean是第一次過來,默認是falsenewSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.// 再努力一次,如果通過簡單工廠創建失敗: 嘗試從單例緩存中,獲取beanName對應的單例beansingletonObject = this.singletonObjects.get(beanName);// 緩存里還是沒有,此時再將異常拋出if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 從singletonsCurrentlyInCreation列表中移出,標志當前beanName對應的bean已經創建完成了。afterSingletonCreation(beanName);}if (newSingleton) {// 看到了嘛,看到了嘛,第一次標志在這里用的,單例緩存入的地方也再這里,哈哈 我們找到啦addSingleton(beanName, singletonObject);}}return singletonObject;}}

這段代碼是Spring框架中用于創建和獲取單例bean實例的一部分。以下是對這段代碼的分析:

  1. 使用lambda表達式:代碼中使用了Java 8的lambda表達式來創建一個匿名的 ObjectFactory 實例。這個 ObjectFactory 會在需要時調用其 getObject() 方法來創建bean。

  2. getSingleton()方法getSingleton(beanName, singletonFactory) 方法被調用來獲取名為 beanName 的單例bean。如果這個bean尚未被創建,singletonFactory.getObject() 將被調用以創建它。

  3. createBean()方法:在lambda表達式中,如果bean不存在,將調用 createBean(beanName, mbd, args) 方法來創建bean。這個方法負責完整的bean創建過程,包括依賴注入、初始化等步驟。

  4. 異常處理:如果在創建bean的過程中拋出了 BeansException(例如,由于循環依賴或其他bean創建問題),代碼將執行以下操作:

    • 調用?destroySingleton(beanName)?來從單例緩存中移除部分創建的bean實例。這是必要的,因為創建過程中可能會因為解決循環依賴而提前將bean放入緩存。
    • 拋出原始的?BeansException,以通知調用者bean創建失敗。
  5. 獲取bean實例:如果bean成功創建,sharedInstance 將包含新創建的bean實例。然后,調用 getObjectForBeanInstance(sharedInstance, name, beanName, mbd) 方法來獲取bean實例對象。這個方法可能執行額外的處理,比如應用 FactoryBean 的邏輯或處理bean的后置處理(post-processing)。

  6. 單例緩存:整個過程中,Spring使用 singletonObjects 作為緩存來存儲已經創建的單例bean。這樣,后續請求相同bean時可以直接從緩存中獲取,而不需要重新創建。

這段代碼體現了Spring框架中創建單例bean的復雜性,包括異常安全、延遲初始化和對循環依賴的處理。通過使用 ObjectFactory 和lambda表達式,Spring能夠以一種靈活且高效的方式來管理bean的生命周期。

createBean

上面我已經找到真正創建bean的地方,也看到Spring是通過簡單工廠創建實例的也驗證了人命常說的bean工廠、工廠模式。同時也看到單例bean首次創建,會放入單例緩存池中。但是整個方法有個內部類,當然這里是Java8的函數式接口實現的,咱們跳出來看下這個默認方法是做了什么邏輯處理。通過這個方法來

看到上圖標注的地方createBean見名知意,應該是這個方法包含了實例化的核心邏輯,咱們進入方法內部一探究竟之前簡單分析一下:在這個內部類里首先會通過createBean來實例化一個bena,如果實例化的過程中出現了異常,就會調用方法destroy Singleton方法,來清除單利bean相關的一系列緩存信息。

/*** Central method of this class: creates a bean instance, 創建bean實例對象* populates the bean instance, applies post-processors, etc. 填充bean實例、應用后置處理器* @see #doCreateBean*/@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.    判斷需要創建的bean是否可以實例化、是否可以通過當前類加載器加載Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// Prepare method overrides. 	準備bean中的方法覆蓋try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.  給BeanPostProcessors一個返回代理而不是目標bean實例的機會。Object bean = resolveBeforeInstantiation(beanName, mbdToUse); // 如果bean配置類后置處理器PostProcessor,則這里返回一個proxy代理對象if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {Object beanInstance = doCreateBean(beanName, mbdToUse, args); // bean實例對象創建方法if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}

?

總結

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

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

相關文章

.profile腳本

.profile 是一個用戶級別的 shell 啟動腳本&#xff0c;用于初始化用戶環境設置&#xff0c;通常用于配置環境變量、設置路徑、運行初始化命令等。以下是一個示例 if [ "$BASH" ]; thenif [ -f ~/.bashrc ]; then. ~/.bashrcfi fimesg n || true 這段代碼通常出現在…

計算器原生js

目錄 1.HTML 2.CSS 2.JS 4.資源 5.運行截圖 6.下載連接 7.注意事項 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

【C++】引用變量詳解

&#x1f4e2;博客主頁&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01; &#x1f4e2;本文由 JohnKi 原創&#xff0c;首發于 CSDN&#x1f649; &#x1f4e2;未來很長&#…

文檔去重(TF-IDF,MinHash, SimHash)

2個doc有些相似有些不相似&#xff0c;如何衡量這個相似度&#xff1b; 直接用Jaccard距離&#xff0c;計算量太大 TF-IDF: TF*IDF TF&#xff1a;該詞在該文檔中的出現次數&#xff0c; IDF&#xff1a;該詞在所有文檔中的多少個文檔出現是DF&#xff0c;lg(N/(1DF)) MinHash …

數據分析_計劃

我做大數據的有6年了&#xff0c;以前都是用sql&#xff0c;或者spark&#xff0c;java&#xff0c;scala&#xff0c;python去做。現在這些平臺搭建、維護、大多數都是搭建一次就完了&#xff0c;而且維護大多是大廠直接用云平臺去做。ETL也是就做一次就夠了&#xff0c;我們公…

基于JAVA+SpringBoot+Vue+Uni-app前后端分離的校園好物小紅書分享平臺小程序

?全網粉絲20W,csdn特邀作者、博客專家、CSDN新星計劃導師、java領域優質創作者,博客之星、掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域和畢業項目實戰? &#x1f345;文末獲取項目下載方式&#x1f345; 一、項目背景介紹&#xff1a; 在快速數字化的時代背…

同三維T80004EA編解碼器視頻使用操作說明書:高清HDMI編解碼器,高清SDI編解碼器,4K超清HDMI編解碼器,雙路4K超高清編解碼器

同三維T80004EA編解碼器視頻使用操作說明書&#xff1a;高清HDMI編解碼器&#xff0c;高清SDI編解碼器&#xff0c;4K超清HDMI編解碼器&#xff0c;雙路4K超高清編解碼器 同三維T80004EA編解碼器視頻使用操作說明書&#xff1a;高清HDMI編解碼器&#xff0c;高清SDI編解碼器&am…

UniVue@v1.3.0版本發布

GitHub倉庫 發布版本倉庫&#xff1a;https://github.com/Avalon712/UniVue 開發版本倉庫&#xff1a;https://github.com/Avalon712/UniVue-Develop UniVue拓展框架UniVue源生成器倉庫&#xff1a;https://github.com/Avalon712/UniVue-SourceGenerator v1.3.0版本新增功能…

DangerWind-RPC-framework---四、SPI

SPI 即 Service Provider Interface &#xff0c;可以理解為專門提供給服務提供者或者擴展框架功能的開發者去使用的一個接口。SPI 將服務接口和具體的服務實現分離開來&#xff0c;將服務調用方和服務實現者解耦&#xff0c;能夠提升程序的擴展性、可維護性。修改或者替換服務…

etcd 實現分布式鎖

10 基于 Etcd 的分布式鎖實現原理及方案

如何通過兔子和窩窩的故事理解“在機器人學習和研究中的獲得成本與維護成本”(節選)

獲得成本 掌握一門課程&#xff0c;以最為簡單的學校成績過60為例&#xff0c;需要按要求提交材料&#xff0c;包括作業、報告、實驗和考試等&#xff0c;依據學分和考核要求的不同&#xff0c;需要對于花費時間和經歷進行完成。 維護成本 考完了&#xff0c;如果被動學習那…

docker拉取鏡像-配置阿里云鏡像加速

1、配置阿里云鏡像&#xff08;用于拉取鏡像加速&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo syst…

Docker 使用基礎(4)—存儲卷

&#x1f3ac;慕斯主頁&#xff1a;修仙—別有洞天 ??今日夜電波&#xff1a;秒針を噛む—ずっと真夜中でいいのに。 0:34━━━━━━?&#x1f49f;──────── 4:20 &#x1f504; ?? ? …

JVM堆內存的結構,YGC,FGC的原理

JVM堆內存結構&#xff1a; JVM堆內存可分為三個區域&#xff1a;新生代&#xff08;Young Generation&#xff09;、年老代&#xff08;Tenured Generation&#xff0c;也叫做Old Generation&#xff09;和永久代&#xff08;Permanent Generation&#xff0c;也叫做Method Ar…

linux 設置nginx開機自啟

1、關閉當前nginx運行 systemctl stop nginx 2、添加以下內容到nginx.service文件&#xff0c;注意nginx.pid文件的路徑&#xff0c;要替換哦&#xff01; vim /etc/systemd/system/nginx.service [Unit] DescriptionThe NGINX HTTP and reverse proxy server Afternetwork…

ArcGIS如何快速對齊兩個圖層

1、問題 如何讓兩個圖層快速對齊 2、使用捕捉工具 移動點或折點&#xff0c;使其與其他要素的折點、邊或端點精確重合。 可指定捕捉規則來控制是將輸入折點捕捉到指定距離范圍內的最近折點、邊還是端點。

MySQL數字相關數據處理函數

目錄 1. 隨機數生成 rand ( ) 2. 四舍五入 round&#xff08;&#xff09; 3. 舍去 truncate ( ) 4. 向上/下取整 5. 空處理 ifnull&#xff08; x , y &#xff09; 1. 隨機數生成 rand ( ) rand ( ) 生成 0 到 1 的隨機數&#xff1b; rand ( x ) 生成 0 到 1 的隨機數…

簡單理解Lua 協程(coroutine)

也許更好的閱讀體驗 協程簡單理解為可以暫停的線程&#xff0c;但是同一時刻只有一個協程可以處于運行狀態。 文章目錄 coroutine.create()coroutine.resume()coroutine.wrap()coroutine.yield()coroutine.resume()參數傳遞resume和yield之間互換數據 coroutine.create() lua…

403 禁止錯誤: 它是什么?如何修復?

您應該對403錯誤代碼很熟悉&#xff01;這種錯誤會導致流量損失&#xff0c;甚至錯失一些商業機會&#xff01; 什么&#xff1f;您在自己的網站上遇到了403錯誤&#xff1f;請立即修復它&#xff01;但是什么原因導致這種錯誤&#xff1f;該如何解決&#xff1f;這兩個問題都…

66種智能優化算法和改進優化算法優化BP神經網絡【開源代碼!】【文末福利IT學習資料】

前言 熟話說得好&#xff0c;創新點不夠&#xff0c;智能優化算法來湊&#xff0c;不要覺得羞恥&#xff0c;因為不僅我們這么干&#xff0c;很多外國人也這么干&#xff01;因為創新點實在太難想了&#xff0c;和優化算法結合下是最簡單的創新點了&#xff01; 之前給大家分享…