【Spring專(zhuān)場(chǎng)】「IOC容器」不看源碼就帶你認(rèn)識(shí)核心流程以及運(yùn)作原理

這是史上最全面的Spring的核心流程以及運(yùn)作原理的分析指南

  • ??【Spring核心專(zhuān)題】「IOC容器篇」不看繁瑣的源碼就帶你瀏覽Spring的核心流程以及運(yùn)作原理

  • ??【Spring核心專(zhuān)題】「AOP容器篇」不看繁瑣的源碼就帶你瀏覽Spring的核心流程以及運(yùn)作原理

  • ??【Spring核心專(zhuān)題】「MVC容器篇」不看繁瑣的源碼就帶你瀏覽Spring的核心流程以及運(yùn)作原理

學(xué)好Spring技術(shù)的背景

針對(duì)于每一個(gè)Java的愛(ài)好者而言,無(wú)論是從事面向于微服務(wù)架構(gòu)技術(shù)的領(lǐng)域(SpringCloud、SpringCloud-Alibaba等),還是面向于傳統(tǒng)互聯(lián)網(wǎng)行業(yè)(SpringBoot)以及軟件系統(tǒng)(Spring\SpringBatch)領(lǐng)域,掌握好Spring框架技術(shù)原理和源碼對(duì)排查問(wèn)題以及未來(lái)的面試技術(shù)有著非常重要的幫助和影響,而接下來(lái),筆者會(huì)針對(duì)于Spring的技術(shù)框架的核心源碼流程點(diǎn)進(jìn)行相關(guān)的分析和認(rèn)識(shí),相信閱讀完本篇文章,一定會(huì)對(duì)Spring的源碼和執(zhí)行原理有著很大的幫助和提升。

分析框架核心流程

獲取Spring框架的IOC容器

IOC容器執(zhí)行流程主要核心流程點(diǎn):

  • 獲取單例Bean對(duì)象
  • 創(chuàng)建單例Bean對(duì)象
  • 創(chuàng)建原始Bean對(duì)象
  • 解決循環(huán)依賴(lài)
  • 填充屬性信息
  • 初始化Bean對(duì)象
getBean方法的執(zhí)行流程
  1. 第一步將beanName或者BeanType類(lèi)型進(jìn)行獲取相關(guān)的容器數(shù)據(jù)對(duì)象,例如:處理以&符號(hào)開(kāi)頭的name名稱(chēng)數(shù)據(jù),以及根據(jù)相關(guān)的alias別名。
  2. 第二步將存在根據(jù) 名稱(chēng)或者別名進(jìn)行獲取相關(guān)的緩存池找那個(gè)進(jìn)行獲取相關(guān)的對(duì)象實(shí)例
    • 如果存在:Spring框架會(huì)調(diào)用getObjectForBeanInstance方法,返回對(duì)應(yīng)的Bean實(shí)例對(duì)象,其中Bean實(shí)例的類(lèi)型有兩種模式:?jiǎn)卫J胶驮湍J?
      • 單例模式:緩存中沒(méi)有,創(chuàng)建一個(gè),然后放入緩存中,其中會(huì)對(duì)該單例對(duì)象bean進(jìn)行先關(guān)的攔截和后置工作。
      • 原型模式:每次都會(huì)創(chuàng)建新的對(duì)象進(jìn)行返回相關(guān)的對(duì)象。
    • 如果當(dāng)前的容器中,無(wú)法獲取到相關(guān)的對(duì)應(yīng)的BeanName的對(duì)象實(shí)例,則會(huì)進(jìn)行想父容器進(jìn)行尋找對(duì)應(yīng)的對(duì)象Bean實(shí)例,如果父容器中存在,直接返回父容器中的數(shù)據(jù)對(duì)象實(shí)例,但是如果父容器還不存在,則會(huì)進(jìn)行創(chuàng)建Bean對(duì)象實(shí)例了,但是在創(chuàng)建之前,會(huì)進(jìn)行解析兩種特殊的Bean操作關(guān)系。
兩種特殊的Bean實(shí)例的關(guān)聯(lián)關(guān)系
  • parent bean的繼承關(guān)系,例如,a bean對(duì)象可以在xml文件中繼承相關(guān) a-parent bean的屬性以及相關(guān)的覆蓋操作

  • 處理相關(guān)的depend-ons依賴(lài)關(guān)系操作,這樣子可以根據(jù)依賴(lài)關(guān)系,建立一個(gè)加載和創(chuàng)建Bean之間的前后關(guān)系和依賴(lài)關(guān)系,例如A depend-ons B的bean對(duì)象,那么在創(chuàng)建A之前一定會(huì)先加載和創(chuàng)建B,依此類(lèi)托。

之后進(jìn)行相關(guān)的創(chuàng)建bean的操作控制!

獲取Spring框架的變量容器
  • singletonObjects:?jiǎn)卫患?jí)緩存池-用于存放完全實(shí)例化+初始化好的對(duì)象Bean,如果從該緩存池中取出的Bean可以直接的使用。

  • earlySingletonObject:?jiǎn)卫?jí)緩存池-用于存放正在初始化的對(duì)象bean,主要用于解決循環(huán)依賴(lài)的臨時(shí)存放的對(duì)象池。

  • singletonFactories:用于存放bean對(duì)象的工廠對(duì)象機(jī)制,主要用于創(chuàng)建bean對(duì)象的ObjectFactory。

createBean方法的執(zhí)行流程
  • createBean的方法入口,getSIngleton方法:
    1. 先從singletonObjects集合中獲取相關(guān)的Bean實(shí)例,若不為空,則直接返回。
    2. 如果獲取不到相關(guān)的對(duì)象實(shí)例在一級(jí)單例緩存池中,則會(huì)進(jìn)行createBeanInstance實(shí)例階段(此部分,接下來(lái)會(huì)詳細(xì)介紹),會(huì)將對(duì)應(yīng)的BeanName添加到singleCurrentlyInCreation集合中,這個(gè)集合主要用于存放相關(guān)的將要?jiǎng)?chuàng)建的對(duì)象bean,這個(gè)是第一步。
    3. 當(dāng)通過(guò)getObject方法調(diào)用createBean方法的是創(chuàng)建實(shí)例對(duì)象的完成之后,會(huì)將對(duì)象實(shí)例從singleCurrentlyInCreation集合中進(jìn)行轉(zhuǎn)移到singleObjects對(duì)象集合緩存池中,映射關(guān)系為:beanName->singleObject對(duì)象。
createBean的方法要點(diǎn)

解析Bean的類(lèi)型和屬性類(lèi)型特點(diǎn)分析,主要分為以下幾點(diǎn)內(nèi)容:

  1. 解析相關(guān)的Bean對(duì)象的類(lèi)型。
  2. 校驗(yàn)和分析處理相關(guān)的override注解修飾的方法,主要用于先去校驗(yàn)和分析是否存在重載方法或者覆蓋方法,方便cglib動(dòng)態(tài)代理的時(shí)候不需要進(jìn)行校驗(yàn),而是直接處理調(diào)用即可。
    • 其中有一個(gè)屬性:lookup-method,如果我們希望在單例對(duì)象里面加入一個(gè)原型模式(prototype)的對(duì)象屬性,那么可以考慮使用<lookup-method name="getPrototypeBean", bean = "prototypeBean" /> ApplicationContextAware。
  3. bean實(shí)例化前的后置處理控制hook鉤子函數(shù)以及相關(guān)回調(diào)機(jī)制控制。
createBean的最核心方法doCreateBean
  • 調(diào)用doCreateBean創(chuàng)建bean實(shí)例,此方法算是最底層的創(chuàng)建createBean的代表方法了,首先他會(huì)遵循從緩存中區(qū)獲取相關(guān)的BeanWrapper實(shí)現(xiàn)類(lèi)對(duì)象,并且清除一些臨時(shí)數(shù)據(jù)信息。
  • 如果緩存中沒(méi)有相關(guān)的緩存,則會(huì)進(jìn)行手動(dòng)創(chuàng)建bean實(shí)例對(duì)象,將實(shí)例對(duì)象包裹在BeanWrapper實(shí)例類(lèi)對(duì)象并且返回該BeanWrapper對(duì)象。
  • 并且采用MergeBeanDefinitionBeanPostProcessor的后置處理器,對(duì)相關(guān)的對(duì)象的abstract和parent的繼承關(guān)系的bean進(jìn)行合并處理。
  • 根據(jù)系統(tǒng)的配置是否支持循環(huán)依賴(lài)的選項(xiàng),進(jìn)行選擇和決定是否采用提前暴露bean的早期引用(early reference),主要用于處理的循環(huán)依賴(lài)。
  • 之后對(duì)相關(guān)的提前暴露的引用和屬性字段進(jìn)行使用popluateBean方法進(jìn)行引用的屬性進(jìn)行填充,其中也包含了相關(guān)的循環(huán)引用的概念在里面。
  • 調(diào)用相關(guān)的initializeBean方法完成余下的初始化工作任務(wù),包含了:initializeBean接口實(shí)現(xiàn)、@PostConstruct注解處理控制、以及init-method方法的屬性處理。
  • 注冊(cè)銷(xiāo)毀相關(guān)的distroy-method的屬性以及相關(guān)的preDestory的方法控制。
doCreateBean創(chuàng)建最原始的Bean對(duì)象

主要通過(guò)createBeanInstance方法實(shí)例機(jī)制,其核心流程為:

  • 檢測(cè)類(lèi)的訪問(wèn)權(quán)限,若禁止訪問(wèn),則會(huì)拋出異常機(jī)制。
  • 如果該對(duì)象bean的factory-method屬性包含了factory工廠方法機(jī)制不為空,則通過(guò)該定義的聲明的相關(guān)的factory方法進(jìn)行創(chuàng)建bean,并且返回結(jié)果。
通過(guò)相關(guān)的構(gòu)造器的方式進(jìn)行構(gòu)建對(duì)象

在此我們會(huì)采用construct的方式進(jìn)行反射進(jìn)行構(gòu)建實(shí)例對(duì)象,并且返回對(duì)象的對(duì)象結(jié)果,步驟如下:

  1. 創(chuàng)建相關(guān)的BeanWrapperImpl對(duì)象作為先關(guān)的Bean實(shí)例對(duì)象的包裝實(shí)現(xiàn)類(lèi)。
  2. 之后需要進(jìn)行構(gòu)建相關(guān)的真實(shí)的原始模型對(duì)象,其中上面說(shuō)了,如果該bean定義擁有相關(guān)的factory方法,則會(huì)直接通過(guò)factory方法建立,否則會(huì)采用構(gòu)造器的方式進(jìn)行構(gòu)建哦!
  3. 會(huì)針對(duì)于該對(duì)象的所有定義以及隱含的構(gòu)造器進(jìn)行分析和處理,采用minOrArg方式計(jì)算出,進(jìn)行分析出了一個(gè)按照參數(shù)數(shù)量進(jìn)行排序的構(gòu)造器列表。(其中會(huì)包含著訪問(wèn)優(yōu)先級(jí)以及參數(shù)個(gè)數(shù)的條件進(jìn)行排序)。
  4. 一般默認(rèn)而言,會(huì)使用最少參數(shù)的構(gòu)造器,當(dāng)然如果存在默認(rèn)構(gòu)造器,一般會(huì)采用默認(rèn)構(gòu)造器區(qū)進(jìn)行處理,但是如果存在非默認(rèn)的構(gòu)造器,則會(huì)采用參數(shù)注入的方式進(jìn)行構(gòu)造器進(jìn)行構(gòu)建。
  5. 核心: 我們前面已經(jīng)將構(gòu)造器列表進(jìn)行排序完成后,會(huì)進(jìn)行篩選獲取合適的構(gòu)造器進(jìn)行執(zhí)行構(gòu)建對(duì)象。如果我們獲取到了一個(gè)含有參數(shù)的構(gòu)造器,那么spring框架會(huì)怎么做?
    • 先進(jìn)行獲取相關(guān)構(gòu)造器中的所有相關(guān)的形式參數(shù)的名稱(chēng)以及類(lèi)型。
    • 在進(jìn)行解析參數(shù),此解析方式會(huì)將對(duì)一些已經(jīng)保存在容器中的數(shù)據(jù)進(jìn)行解析注入以及相關(guān)的類(lèi)型參數(shù)轉(zhuǎn)換機(jī)制。
    • 從而計(jì)算構(gòu)造器與數(shù)值類(lèi)型的差異性,選擇最佳何時(shí)的構(gòu)造器方法。
    • 當(dāng)我們已經(jīng)篩選出和是的構(gòu)造方法(最終),如果在此使用創(chuàng)建bean對(duì)象實(shí)例的時(shí)候,可以直接使用,無(wú)需在進(jìn)行篩選。
    • 之后我們采用初始化策略進(jìn)行構(gòu)建該實(shí)例bean對(duì)象。
    • 最后將該對(duì)象注入到我們的BeanWrapperImpl對(duì)象模型中,并返回對(duì)象。
如果通過(guò)構(gòu)造器或者工廠方法都無(wú)法構(gòu)建

那么會(huì)采用組合方式進(jìn)行構(gòu)建該對(duì)象

  1. 通過(guò)工廠方法進(jìn)行構(gòu)建
  2. 通過(guò)自定義構(gòu)造器進(jìn)行構(gòu)建
  3. 通過(guò)默認(rèn)構(gòu)造器進(jìn)行構(gòu)建

構(gòu)建的方式需要配合動(dòng)態(tài)代理機(jī)制

為了方便我們進(jìn)行在對(duì)Springbean容器的對(duì)象進(jìn)行AOP攔截操作處理機(jī)制。

image

解決循環(huán)依賴(lài)

話(huà)不多說(shuō),就是提前暴露,可以通過(guò)factory避過(guò)去以及@lazy不會(huì)引起錯(cuò)誤等。

IOC容器篇

主要的方法為populateBean方法

popluteBean的方法的執(zhí)行流程

首先會(huì)獲取相關(guān)的注入該類(lèi)對(duì)象bean的屬性列表,我們?cè)偾卸x為pvs。

  1. 當(dāng)構(gòu)造器構(gòu)建完對(duì)象之后會(huì)進(jìn)行相關(guān)的自定義屬性進(jìn)行填充,但是在進(jìn)行相關(guān)的屬性填充進(jìn)行之前,會(huì)先去嘗試采用系統(tǒng)默認(rèn)后置處理器進(jìn)行填充。

主要通過(guò)參數(shù)名或者參數(shù)類(lèi)型進(jìn)行解析并且填充相關(guān)的依賴(lài)屬性,主要可以通過(guò)的手段就是@Autowired或者@Resource、@Inject等。

  1. 之后還會(huì)在采用后置處理器對(duì)屬性進(jìn)行動(dòng)態(tài)pvs的內(nèi)容進(jìn)行填充處理。

  2. 會(huì)將屬性應(yīng)用到bean中的applyProperyValues方法:

    • 在檢測(cè)屬性值是否已經(jīng)完成轉(zhuǎn)換,如果該屬性值已經(jīng)完成轉(zhuǎn)換,則直接使用,無(wú)需再次轉(zhuǎn)換。
    • 遍歷屬性列表,解析器屬性的原始值,在通過(guò)PropertisSourcePlaceholdConfigurer進(jìn)行相關(guān)的解析操作,并且完成解析值resolveValue。
  3. 最后將的到的解析數(shù)值resolveValue進(jìn)行相關(guān)的類(lèi)型屬性轉(zhuǎn)換操作。

  4. 將類(lèi)型轉(zhuǎn)換后的值設(shè)置到PropertyValue對(duì)象中,將PropertyValue對(duì)象存入deepCopy集合中,并且將deepCopy的屬性值注入到bean對(duì)象中。

根據(jù)名稱(chēng)和類(lèi)型進(jìn)行填充
根據(jù)名稱(chēng)注入

就是單純的將bean名稱(chēng)進(jìn)行注入到相關(guān)的非簡(jiǎn)單類(lèi)型的注入機(jī)制。

根據(jù)類(lèi)型注入
  • 主要處理@Value注解進(jìn)行注入操作解析機(jī)制!
  • 解析數(shù)組、list、map等類(lèi)型的依賴(lài)注入機(jī)制
  • 根據(jù)類(lèi)型查找相關(guān)何時(shí)的類(lèi)型數(shù)據(jù)信息
  • 如果候選項(xiàng)的數(shù)量為0,則拋出異常。如果=1,則直接從候選列表中進(jìn)行獲取,如果>1,則在多個(gè)候選選項(xiàng)中的獲取最優(yōu)的對(duì)象,否則拋出異常。
  • 如果候選選選為class類(lèi)型,則標(biāo)識(shí)候選選選還沒(méi)有完成實(shí)例化,此時(shí)通過(guò)BeanFactory.getBean的方式進(jìn)行實(shí)例化,否則會(huì)直接返回對(duì)象實(shí)例。

初始化Bean對(duì)象

主要是經(jīng)歷了所有的實(shí)例化和處理之后,則會(huì)需要進(jìn)行相關(guān)的初始化方法的調(diào)用,在底層框架表現(xiàn)為initializeBean方法進(jìn)行初始化,執(zhí)行順序的判斷邏輯執(zhí)行流程為:

  1. 檢測(cè)bean是否實(shí)現(xiàn)了xAware類(lèi)型的接口,如果實(shí)現(xiàn)了,則會(huì)向該bean中注入相關(guān)的x的實(shí)例屬性對(duì)象,主要通過(guò)調(diào)用invokeAwareMethods方法。
  2. 之后開(kāi)始執(zhí)行初始化的前置操作:例如BeanPostProcessor以及相關(guān)的afterPropertiesSetting方法。
  3. 執(zhí)行相關(guān)的初始化操作invokeInitMethods方法。
  4. 執(zhí)行后置的初始化操作,例如BeanPostProcessor的后置處理機(jī)制操作。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容