SpringBoot?Bean實例化流程解析
前置工作
新建一個RestService,代碼如下
package geek.springboot.application.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; /** * Rest Service * * @author Bruse */ @Slf4j @Service public class RestService { @PostConstruct public void init() { log.info("RestService init...."); } }
finishBeanFactoryInitialization
來到 AbstractApplicationContext
的 refresh()
方法,在該方法中調(diào)用了 finishBeanFactoryInitialization()
方法
判斷是否存在conversionService
首先判斷當前IOC容器中是否存在 conversionService
,有的話將其設(shè)置到 BeanFactory
中
判斷是否已有BeanFactoryPostProcessor
接下來判斷當前 BeanFactory
中是否存在 BeanFactoryPostProcessor
,沒有的話注冊一個默認的實現(xiàn)
初始化LoadTimeWeaverAware
接著查詢當前IOC容器中是否存在 LoadTimeWeaverAware
的實現(xiàn),有的話則進行初始化。該類是使用AOP時做織入的一個工具類,一般情況下開發(fā)不會使用到它,所以這里其實并不會執(zhí)行到循環(huán)體中的 getBean()
停止使用TempClassLoader
接下來將 BeanFactory
的 tempClassLoader
屬性設(shè)置為null
調(diào)用freezeConfiguration()
接下來調(diào)用 freezeConfiguration()
方法,該方法主要是做一個 凍結(jié)
聲明,即聲明在此期間無法再向 BeanFactory
注冊新的Bean定義
調(diào)用preInstantiateSingletons()
最后也是最關(guān)鍵的,調(diào)用 preInstantiateSingletons()
方法進行Bean的實例化
實例化Bean
最終其實會調(diào)用到 DefaultListableBeanFactor
的 preInstantiateSingletons()
方法。
首先會獲取當前所有Bean定義的名稱,并進行遍歷
Tips:除了之前定義的service,controller,config...也包含了Spring內(nèi)置的一些Bean
接著根據(jù)名稱獲取相關(guān)的 BeanDefinition
,也就是Bean的定義,它包括了Bean的一些元信息,比如是否單例,是否延遲初始化,該Bean依賴項等。
Tips:這里為了避免混淆視聽,我事先已經(jīng)在debug斷點處加上了觸發(fā)條件,只有beanName是restService時,斷點才生效。
可以看到Spring會先根據(jù) BeanDefinition
判斷出當前類是否 抽象類
,是否 單例
,是否 延遲加載
。只有當當前 BeanDefinition
滿足既不是 抽象類
,也不 延遲加載
的 單例
時,才會進行操作。
接著還會判斷當前 Bean
是否實現(xiàn)了 FactoryBean
的接口,是的話則還要對 Bean
做一些處理。
因為之前創(chuàng)建的 RestService
沒有實現(xiàn) FactoryBean
接口,所以直接調(diào)用 getBean
方法
doGetBean
接著走到 AbstractBeanFactory
的 doGetBean()
方法,該方法主要作用就是返回一個指定的Bean實例
transformedBeanName
首先是對 Bean
的名稱做一個處理,其中包含了若 BeanName
中包含了 FACTORY_BEAN_PREFIX
的話,則將其從 BeanName
中刪除的邏輯
同時如果有設(shè)置 別名
的話,也會獲取真正的 BeanName
再返回
getSingleton
接著進入 DefaultSingletonBeanRegistry
的 getSingleton
方法,該方法主要作用是 返回給定名稱注冊的單例對象
雖然這里邏輯很長,還用到了 雙檢鎖
機制,但是其實從 singletonObjects
中檢查是否有對應(yīng) beanName
的 Bean
存在時,因為是第一次創(chuàng)建,所以 singletonObjects
中并不存在相關(guān)的 Bean
,直接沒進 if
方法體,就返回 null
了
isPrototypeCurrentlyInCreation
接著執(zhí)行 AbstractBeanFactory
的 isPrototypeCurrentlyInCreation
方法,該方法主要是檢查該 beanName
相關(guān)的 Bean
是否在當前線程創(chuàng)建中,因為我們這里還沒有進行創(chuàng)建,所以這里方法返回false。
獲取BeanFactory
接著嘗試獲取父類 BeanFactory
,并調(diào)用父類 BeanFactory
的 getBean
方法獲取 Bean
,但是因為當前的 beanFactory
已經(jīng)是最頂級的了,所以直接跳過大段代碼。
Tips:Spring中BeanFactory跟JVM實現(xiàn)雙親委派機制的ClassLoader一樣,也存在子級父級關(guān)系
markBeanAsCreated
接著執(zhí)行 markBeanAsCreated
方法,這里也用到了 雙檢鎖
機制,方法很簡單,就是將 beanName
添加到 alreadyCreated
當中,算是做一個標識,標識當前 beanName
對應(yīng)的 Bean
正在創(chuàng)建當中。
getMergedLocalBeanDefinition
接著調(diào)用 getMergedLocalBeanDefinition
方法獲取 BeanDefinition
,并檢查是否是抽象類,是的話直接拋出異常
檢查是否存在依賴
接著檢查當前要實例化的 Bean
是否和別的Bean存在依賴關(guān)系,是的話得先把所依賴的 Bean
創(chuàng)建好,才能繼續(xù)實例化當前的 Bean
。
因為 RestService
中沒有依賴什么別的 Bean
,所以這里略過一段代碼
根據(jù)作用域創(chuàng)建Bean
接著便是根據(jù)作用域的不同,使用不同方式創(chuàng)建 Bean
getSingleton
這里進入 DefaultSingletonBeanRegistry
的 getSingleton
方法
這里首先還是會檢查當前 Bean
是否已初始化,是的話直接返回
createBean
接著調(diào)用 ObjectFactory
的 getObject
方法,因為傳參時是傳遞了一個 匿名內(nèi)部類
,所以重新回到 AbstractBeanFactory
,可以看到調(diào)用了 createBean
方法
resolveBeanClass
接著來到 AbstractAutowireCapableBeanFactory
的 createBean
方法,首先會調(diào)用 AbstractBeanFactory
的 resolveBeanClass
方法對 BeanDefinition
對應(yīng)的 Class
做一個解析,這里因為之前已經(jīng)過了,所以直接返回。
resolveBeforeInstantiation
接著來到 resolveBeforeInstantiation
方法,可以看到如果它返回的 Bean
不為空,那么將直接返回,意味著 Bean
實例化完成。
接著深入查看具體實現(xiàn)細節(jié),可以看到會判斷當前IOC容器是否存在 InstantiationAwareBeanPostProcessor
接口的實現(xiàn)
如果存在 InstantiationAwareBeanPostProcessor
接口實現(xiàn),則會調(diào)用其 postProcessBeforeInstantiation
方法,如果該方法返回值不為空,那么直接返回,并且調(diào)用 postProcessAfterInitialization
方法再對返回值做一些處理
因為當前項目中并沒有存在 InstantiationAwareBeanPostProcessor
接口實現(xiàn),所以直接返回的是 null
doCreateBean
所以初始化 Bean
的重任還是交給了 doCreateBean
方法
首先判斷當前 Bean
是否單例,是的話將其從 factoryBeanInstanceCache
中移除
createBeanInstance
接著進入 createBeanInstance
方法,首先調(diào)用 getInstanceSupplier
方法判斷當前 BeanDefinition
是否從其它配置加載的,然后調(diào)用 getFactoryMethodName
方法判斷當前 BeanDefinition
是否存在工廠方法, RestService
兩個條件都不滿足,所以會一路執(zhí)行到后續(xù)代碼。
determineConstructorsFromBeanPostProcessors
接著執(zhí)行到 determineConstructorsFromBeanPostProcessors
方法,該方法主要是 確定使用哪個構(gòu)造器來初始化Bean
可以看到內(nèi)部實現(xiàn)其實是依靠調(diào)用 SmartInstantiationAwareBeanPostProcessor
的 determineCandidateConstructors
方法來進行確定的
但其實最后返回的是 null
instantiateBean
最后來到 instantiateBean
方法,由注釋也可以看出,該方法就是在 Bean
無需做特殊處理,調(diào)用默認無參構(gòu)造函數(shù)即可初始化時調(diào)用的。
首先調(diào)用 getInstantiationStrategy
方法獲取 實例化策略
,可以看到默認 實例化策略
是 CglibSubclassingInstantiationStrategy
instantiate
接著調(diào)用 instantiate
方法,在該方法中利用反射機制獲取 類默認構(gòu)造函數(shù)
BeanUtils.instantiateClass
最后調(diào)用 BeanUtils
的 instantiateClass
方法進行構(gòu)建,可以看出其實該方法內(nèi)部就是用了Java的 反射機制
進行類的實例構(gòu)建
BeanWrapper
Bean
實例成功創(chuàng)建后,會創(chuàng)建 BeanWrapper
實例來對 Bean
實例做一個包裝,并調(diào)用 initBeanWrapper
方法對 BeanWrapper
進行初始化操作
可以看到最后返回的不是 BeanInstance
,而是把 BeanInstance
給包裹了一層,返回的 BeanWrapper
。
applyMergedBeanDefinitionPostProcessors
接著調(diào)用 applyMergedBeanDefinitionPostProcessors
方法,該方法本質(zhì)即獲取所有 MergedBeanDefinitionPostProcessor
實現(xiàn),并逐個調(diào)用其 postProcessMergedBeanDefinition
方法
populateBean
接著執(zhí)行 populateBean
方法,該方法主要用來填充當前的 BeanInstance
會獲取當前 BeanDefinition
的所有 Property
,并判斷以何種方式進行自動注入,根據(jù)類型?根據(jù)名稱?
還可以看到會嘗試獲取所有 InstantiationAwareBeanPostProcessor
實現(xiàn),并調(diào)用其 postProcessProperties
方法
總結(jié)
在Spring實例化Bean的過程中, BeanDefinition
幾乎貫穿了整個流程,而 BeanDefinition
是一個對象在Spring中的描述,Spring通過操作 BeanDefinition
來完成 Bean
的實例化和屬性注入,而實例化的過程中又使用到了Java中非?;A(chǔ)且重要的—— 反射
以上就是SpringBoot Bean實例化流程解析的詳細內(nèi)容,更多關(guān)于SpringBoot Bean實例化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解析Java編程中對于包結(jié)構(gòu)的命名和訪問
這篇文章主要介紹了Java編程中對于包結(jié)構(gòu)的命名和訪問,是Java入門學習中的基礎(chǔ)知識,需要的朋友可以參考下2015-12-12SpringMVC集成Web與MVC執(zhí)行流程和數(shù)據(jù)響應(yīng)及交互相關(guān)介紹全面總結(jié)
Spring MVC 是 Spring 提供的一個基于 MVC 設(shè)計模式的輕量級 Web 開發(fā)框架,本質(zhì)上相當于 Servlet,Spring MVC 角色劃分清晰,分工明細,這篇文章主要介紹了SpringMVC集成Web與MVC執(zhí)行流程和數(shù)據(jù)響應(yīng)及交互2022-10-10Java 實戰(zhàn)項目錘煉之仿天貓網(wǎng)上商城的實現(xiàn)流程
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實現(xiàn)一個仿天貓網(wǎng)上商城項目,大家可以在過程中查缺補漏,提升水平2021-11-11關(guān)于JSON.toJSONString()和Gson.toJson()方法的比較
本文介紹了兩種將Java對象轉(zhuǎn)換為JSON字符串的方法:阿里的`JSON.toJSONString()`和谷歌的`Gson.toJson()`,通過一個示例,展示了當使用繼承關(guān)系且子類覆蓋父類字段時,`Gson`會報錯,而`JSON`可以正常運行,作者建議在處理JSON相關(guān)操作時使用阿里的`JSON`類2024-11-11Java實戰(zhàn)員工績效管理系統(tǒng)的實現(xiàn)流程
只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+Mysql+Maven+HTML實現(xiàn)一個員工績效管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2022-01-01JAVA使用Gson解析json數(shù)據(jù)實例解析
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式,易于閱讀和編寫,同時也易于機器解析和生成。接下來通過本文給大家介紹JAVA使用Gson解析json數(shù)據(jù)實例解析,需要的朋友參考下吧2016-03-03Mybatis-Plus集成Sharding-JDBC與Flyway實現(xiàn)多租戶分庫分表實戰(zhàn)
這篇文章主要為大家介紹了Mybatis-Plus集成Sharding-JDBC與Flyway實現(xiàn)多租戶分庫分表實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11