0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Spring中Bean的生命周期是怎樣的?

jf_ro2CN3Fa ? 來源:樓仔 ? 作者:樓仔 ? 2022-10-11 15:08 ? 次閱讀

1. 基礎知識

1.1 什么是 IoC ?

1.2 Bean 生命周期

1.3 執(zhí)行流程

1.4 擴展方法

2. 源碼解讀

2.1 代碼入口

2.2 實例化

2.3 屬性賦值

2.4 初始化

2.5 銷毀

3. 寫在最后

Spring Bean 的生命周期,面試時非常容易問,這不,前段時間就有個讀者去面試,因為不會回答這個問題,一面都沒有過。

如果只講基礎知識,感覺和網上大多數文章沒有區(qū)別,但是我又想寫得稍微深入一點。

考慮很多同學不喜歡看源碼,我就把文章分為 2 大部分,前面是基礎知識,主要方便大家面試和學習 ,后面是源碼部分,對源碼感興趣的同學可以繼續(xù)往后面看。

不 BB,上文章目錄。

f4755436-4162-11ed-96c9-dac502259ad0.png

1. 基礎知識

1.1 什么是 IoC ?

IoC,控制反轉,想必大家都知道,所謂的控制反轉,就是把 new 對象的權利交給容器,所有的對象都被容器控制,這就叫所謂的控制反轉。

IoC 很好地體現了面向對象設計法則之一 —— 好萊塢法則:“別找我們,我們找你 ”,即由 IoC 容器幫對象找相應的依賴對象并注入,而不是由對象主動去找。

理解好 IoC 的關鍵是要明確 “誰控制誰,控制什么,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”。

f4936020-4162-11ed-96c9-dac502259ad0.png

誰控制誰,控制什么?

傳統(tǒng) Java SE 程序設計,我們直接在對象內部通過 new 進行創(chuàng)建對象,是程序主動去創(chuàng)建依賴對象。而 IoC 是由專門一個容器來創(chuàng)建這些對象,即由 IoC 容器來控制對象的創(chuàng)建。

誰控制誰?當然是 IoC 容器控制了對象;

控制什么?主要控制了外部資源獲取(不只是對象,比如包括文件等)。

為何是反轉,哪些方面反轉了?

有反轉就有正轉,傳統(tǒng)應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉,而反轉則是由容器來幫忙創(chuàng)建及注入依賴對象。

為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;

哪些方面反轉了?依賴對象的獲取被反轉了。

1.2 Bean 生命周期

對 Prototype Bean 來說,當用戶 getBean 獲得 Prototype Bean 的實例后,IOC 容器就不再對當前實例進行管理,而是把管理權交由用戶,此后再 getBean 生成的是新的實例。

所以我們描述 Bean 的生命周期,都是指的 Singleton Bean。

f51a6804-4162-11ed-96c9-dac502259ad0.png

Bean 生命周期過程:

實例化 :第 1 步,實例化一個 Bean 對象;

屬性賦值 :第 2 步,為 Bean 設置相關屬性和依賴;

初始化 :初始化的階段的步驟比較多,5、6 步是真正的初始化,第 3、4 步為在初始化前執(zhí)行,第 7 步在初始化后執(zhí)行,初始化完成之后,Bean 就可以被使用了;

銷毀 :第 8~10 步,第 8 步其實也可以算到銷毀階段,但不是真正意義上的銷毀,而是先在使用前注冊了銷毀的相關調用接口,為了后面第 9、10 步真正銷毀 Bean 時再執(zhí)行相應的方法。

整個執(zhí)行流程稍微有些抽象,下面我們通過代碼,來演示執(zhí)行流程。

1.3 執(zhí)行流程

創(chuàng)建一個 LouzaiBean。

publicclassLouzaiBeanimplementsInitializingBean,BeanFactoryAware,BeanNameAware,DisposableBean{

/**
*姓名
*/
privateStringname;

publicLouzaiBean(){
System.out.println("1.調用構造方法:我出生了!");
}

publicStringgetName(){
returnname;
}

publicvoidsetName(Stringname){
this.name=name;
System.out.println("2.設置屬性:我的名字叫"+name);
}

@Override
publicvoidsetBeanName(Strings){
System.out.println("3.調用BeanNameAware#setBeanName方法:我要上學了,起了個學名");
}

@Override
publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{
System.out.println("4.調用BeanFactoryAware#setBeanFactory方法:選好學校了");
}

@Override
publicvoidafterPropertiesSet()throwsException{
System.out.println("6.InitializingBean#afterPropertiesSet方法:入學登記");
}

publicvoidinit(){
System.out.println("7.自定義init方法:努力上學ing");
}

@Override
publicvoiddestroy()throwsException{
System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
}

publicvoiddestroyMethod(){
System.out.println("10.自定義destroy方法:睡了,別想叫醒我");
}

publicvoidwork(){
System.out.println("Bean使用中:工作,只有對社會沒有用的人才放假。。");
}
}

自定義一個后處理器 MyBeanPostProcessor。

publicclassMyBeanPostProcessorimplementsBeanPostProcessor{

@Override
publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{
System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到學校報名啦");
returnbean;
}

@Override
publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{
System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:終于畢業(yè),拿到畢業(yè)證啦!");
returnbean;
}
}

applicationContext.xml 配置文件(部分)。





測試入口:

publicclassMyTest{
publicstaticvoidmain(String[]args){
ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");
LouzaiBeanlouzaiBean=(LouzaiBean)context.getBean("louzaiBean");
louzaiBean.work();
((ClassPathXmlApplicationContext)context).destroy();
}
}

執(zhí)行結果:

1.調用構造方法:我出生了!
2.設置屬性:我的名字叫樓仔
3.調用BeanNameAware#setBeanName方法:我要上學了,起了個學名
4.調用BeanFactoryAware#setBeanFactory方法:選好學校了
5.BeanPostProcessor.postProcessBeforeInitialization方法:到學校報名啦
6.InitializingBean#afterPropertiesSet方法:入學登記
7.自定義init方法:努力上學ing
8.BeanPostProcessor#postProcessAfterInitialization方法:終于畢業(yè),拿到畢業(yè)證啦!
Bean使用中:工作,只有對社會沒有用的人才放假。。
9.DisposableBean#destroy方法:平淡的一生落幕了
10.自定義destroy方法:睡了,別想叫醒我

這個流程非常清晰,Bean 生命周期流程圖能完全對應起來。

1.4 擴展方法

我們發(fā)現,整個生命周期有很多擴展過程,大致可以分為 4 類:

Aware 接口:讓 Bean 能拿到容器的一些資源,例如 BeanNameAware 的 setBeanName() ,BeanFactoryAware 的 setBeanFactory() ;

后處理器:進行一些前置和后置的處理,例如 BeanPostProcessor 的 postProcessBeforeInitialization()postProcessAfterInitialization()

生命周期接口:定義初始化方法和銷毀方法的,例如 InitializingBean 的 afterPropertiesSet() ,以及 DisposableBean 的 destroy() ;

配置生命周期方法:可以通過配置文件,自定義初始化和銷毀方法,例如配置文件配置的 init()destroyMethod() 。

2. 源碼解讀

注意:Spring 的版本是 5.2.15.RELEASE ,否則和我的代碼不一樣?。。?/p>

上面的知識,網上其實都有,下面才是我們的重頭戲,讓你跟著我走一遍代碼流程。

2.1 代碼入口

f52ef8d2-4162-11ed-96c9-dac502259ad0.pngf5592b2a-4162-11ed-96c9-dac502259ad0.png

這里需要多跑幾次,把前面的 beanName 跳過去,只看 louzaiBean。

f58a2bc6-4162-11ed-96c9-dac502259ad0.pngf5c39cc6-4162-11ed-96c9-dac502259ad0.png

進入 doGetBean(),從 getSingleton() 沒有找到對象,進入創(chuàng)建 Bean 的邏輯。

f5ded540-4162-11ed-96c9-dac502259ad0.pngf614cd62-4162-11ed-96c9-dac502259ad0.png

2.2 實例化

進入 doCreateBean() 后,調用 createBeanInstance()。

f628265a-4162-11ed-96c9-dac502259ad0.png

進入 createBeanInstance() 后,調用 instantiateBean()。

f63fbda6-4162-11ed-96c9-dac502259ad0.pngf65af058-4162-11ed-96c9-dac502259ad0.pngf6687e58-4162-11ed-96c9-dac502259ad0.pngf68253fa-4162-11ed-96c9-dac502259ad0.pngf6b7d0de-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,實例化 LouzaiBean。

f6ca7ec8-4162-11ed-96c9-dac502259ad0.png圖片

2.3 屬性賦值

再回到 doCreateBean(),繼續(xù)往后走,進入 populateBean()。

這個方法非常重要,里面其實就是依賴注入的邏輯,不過這個不是我們今天的重點,大家如果對依賴注入和循環(huán)依賴感興趣,可以翻閱我之前的文章。

f6e63e56-4162-11ed-96c9-dac502259ad0.png

進入 populateBean() 后,執(zhí)行 applyPropertyValues()

f701bd5c-4162-11ed-96c9-dac502259ad0.png

進入 applyPropertyValues(),執(zhí)行 bw.setPropertyValues()

f70dedfc-4162-11ed-96c9-dac502259ad0.pngf737e12a-4162-11ed-96c9-dac502259ad0.pngf75d67a6-4162-11ed-96c9-dac502259ad0.pngf76e0f84-4162-11ed-96c9-dac502259ad0.png

進入 processLocalProperty(),執(zhí)行 ph.setValue()。

f787e59e-4162-11ed-96c9-dac502259ad0.pngf7b03ee0-4162-11ed-96c9-dac502259ad0.pngf7cf9e52-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,給 LouzaiBean 賦值 name。

f7fd9c6c-4162-11ed-96c9-dac502259ad0.png

到這里,populateBean() 就執(zhí)行完畢,下面開始初始化 Bean。

2.4 初始化

我們繼續(xù)回到 doCreateBean(),往后執(zhí)行 initializeBean()。

f82a1116-4162-11ed-96c9-dac502259ad0.pngf847984e-4162-11ed-96c9-dac502259ad0.pngf8594698-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,給 LouzaiBean 設置 BeanName。

f873cdba-4162-11ed-96c9-dac502259ad0.png

回到 invokeAwareMethods()。

f8814026-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,給 LouzaiBean 設置 BeanFactory。

f8e5b308-4162-11ed-96c9-dac502259ad0.png

第一次回到 initializeBean() ,執(zhí)行下面邏輯。

f8f35ff8-4162-11ed-96c9-dac502259ad0.png

這里需要多循環(huán)幾次,找到 MyBeanPostProcessor 的策略方法。

f93dad38-4162-11ed-96c9-dac502259ad0.png

我們自己定義的后置處理方法。

f988cc32-4162-11ed-96c9-dac502259ad0.png

第二次回到 initializeBean() ,執(zhí)行下面邏輯。

f9b3dec2-4162-11ed-96c9-dac502259ad0.pngf9d8cf98-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,執(zhí)行 afterPropertiesSet()。

f9f65ab8-4162-11ed-96c9-dac502259ad0.png

返回 invokeInitMethods(),執(zhí)行下面邏輯。

fa1bbf24-4162-11ed-96c9-dac502259ad0.png

進入 invokeCustomInitMethod(),執(zhí)行下面邏輯。

fa45b18a-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,執(zhí)行 init()。

fa716384-4162-11ed-96c9-dac502259ad0.png

第三次回到 initializeBean() ,執(zhí)行下面邏輯。

fa8e46ca-4162-11ed-96c9-dac502259ad0.pngfaa8e7a0-4162-11ed-96c9-dac502259ad0.png

我們自己定義的后置處理方法。

fb2152da-4162-11ed-96c9-dac502259ad0.png

到這里,初始化的流程全部結束,都是圍繞 initializeBean() 展開。

2.5 銷毀

當 louzaiBean 生成后,后面開始執(zhí)行銷毀操作,整個流程就比較簡單。

fb47debe-4162-11ed-96c9-dac502259ad0.pngfb5f720e-4162-11ed-96c9-dac502259ad0.pngfb80dbba-4162-11ed-96c9-dac502259ad0.pngfba80762-4162-11ed-96c9-dac502259ad0.pngfbcf11b8-4162-11ed-96c9-dac502259ad0.pngfbf49f1e-4162-11ed-96c9-dac502259ad0.pngfc20e506-4162-11ed-96c9-dac502259ad0.pngfc3fcb38-4162-11ed-96c9-dac502259ad0.pngfc6ac6b2-4162-11ed-96c9-dac502259ad0.pngfc820aac-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,執(zhí)行 destroy()。

fca82fb6-4162-11ed-96c9-dac502259ad0.png

回到 destroy(),執(zhí)行下面邏輯。

fcc53da4-4162-11ed-96c9-dac502259ad0.pngfcfc5582-4162-11ed-96c9-dac502259ad0.pngfd9e5bac-4162-11ed-96c9-dac502259ad0.png

走進示例 LouzaiBean 的方法,執(zhí)行 destroyMethod()。

fdd07268-4162-11ed-96c9-dac502259ad0.png

到這里,所有的流程全部結束,文章詳細描述所有的代碼邏輯流轉,你可以完全根據上面的邏輯,自己 debug 一遍。

3. 寫在最后

我們再回顧一下幾個重要的方法:

doCreateBean() :這個是入口;

createBeanInstance() :用來初始化 Bean,里面會調用對象的構造方法;

populateBean() :屬性對象的依賴注入,以及成員變量初始化;

initializeBean() :里面有 4 個方法,

先執(zhí)行 aware 的 BeanNameAware、BeanFactoryAware 接口;

再執(zhí)行 BeanPostProcessor 前置接口;

然后執(zhí)行 InitializingBean 接口,以及配置的 init();

最后執(zhí)行 BeanPostProcessor 的后置接口。

destory() :先執(zhí)行 DisposableBean 接口,再執(zhí)行配置的 destroyMethod()。

對于 populateBean(),里面的核心其實是對象的依賴注入,這里也是??嫉闹R點,比如循環(huán)依賴,大家如果對這塊也感興趣,可以和我交流。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2952

    瀏覽量

    104479
  • 編程
    +關注

    關注

    88

    文章

    3565

    瀏覽量

    93536
  • spring
    +關注

    關注

    0

    文章

    338

    瀏覽量

    14295
  • IOC
    IOC
    +關注

    關注

    0

    文章

    28

    瀏覽量

    10080

原文標題:阿里云面試:Spring 中 Bean 的生命周期是怎樣的?

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    基于Rust語言中的生命周期

    Rust是一門系統(tǒng)級編程語言具備高效、安和并發(fā)等特,而生命周期是這門語言中比較重要的概念之一。在這篇教程,我們會了解什么是命周期、為什么需要生命周期、如何使用
    的頭像 發(fā)表于 09-19 17:03 ?857次閱讀

    Spring-10-bean生命周期

    spring
    電子學習
    發(fā)布于 :2023年01月07日 16:48:18

    09-SpringBean生命周期總結

    spring
    電子學習
    發(fā)布于 :2023年01月14日 09:55:54

    AutoScaling 生命周期掛鉤功能

    實例是伸縮組彈出來的不是手動添加的),然后釋放實例(如果 ECS 實例是伸縮組彈出來的不是手動添加的),并將實例從伸縮組移出。LifecycleHook 通知方式如果生命周期掛鉤配置了通知對象,那么
    發(fā)表于 06-27 17:13

    HarmonyOS應用開發(fā)-PageAbility生命周期

    pageAbility的生命周期如下圖所示:在代碼通過調用下列方法實現生命周期操作:onShow() :Ability由后臺不可見狀態(tài)切換到前臺可見狀態(tài)調用onShow方法,此時用戶在屏幕可以看到
    發(fā)表于 10-17 11:11

    在S32G2 RM中有“生命周期”,生命周期的完整含義是什么?

    在S32G2 RM,有“生命周期”。生命周期的完整含義是什么,我們應該如何使用它?
    發(fā)表于 04-23 10:37

    一文讀懂Android Activity生命周期

    正常情況下Activity的生命周期: Activity的生命周期大概可以歸為三部分 整個的生命周期:onCreate()可以設置所有的“全局”狀態(tài), onDestory()可以釋放所有的資源 可見
    發(fā)表于 05-30 01:03 ?1556次閱讀

    基于延長WSN生命周期的LEACH算法的改進

    基于延長WSN生命周期的LEACH算法的改進(開關電源技術與設計pdf百度云)-基于延長WSN生命周期的LEACH算法的改進? ? ? ? ? ? ? ? ? ??
    發(fā)表于 09-15 11:17 ?14次下載
    基于延長WSN<b class='flag-5'>生命周期</b>的LEACH算法的改進

    bean放入Spring容器中有哪些方式

    bean放入Spring容器中有哪些方式?
    的頭像 發(fā)表于 09-19 15:25 ?684次閱讀

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實例從創(chuàng)建到銷毀的過程.
    的頭像 發(fā)表于 02-06 16:16 ?833次閱讀
    Vue入門Vue的<b class='flag-5'>生命周期</b>

    編譯器的標準生命周期

    編譯器的標準生命周期
    發(fā)表于 03-14 19:06 ?0次下載
    編譯器的標準<b class='flag-5'>生命周期</b>

    編譯器的標準生命周期

    編譯器的標準生命周期
    發(fā)表于 07-05 19:32 ?0次下載
    編譯器的標準<b class='flag-5'>生命周期</b>

    數據包的生命周期

    電子發(fā)燒友網站提供《數據包的生命周期.pdf》資料免費下載
    發(fā)表于 10-13 14:44 ?0次下載

    鴻蒙開發(fā):【PageAbility的生命周期

    PageAbility生命周期是PageAbility被調度到INACTIVE、ACTIVE、BACKGROUND等各個狀態(tài)的統(tǒng)稱。PageAbility生命周期流轉及狀態(tài)說明見如下圖1、表1所示。
    的頭像 發(fā)表于 06-17 10:05 ?650次閱讀
    鴻蒙開發(fā):【PageAbility的<b class='flag-5'>生命周期</b>】

    鴻蒙開發(fā)組件:DataAbility的生命周期

    應用開發(fā)者可以根據業(yè)務場景實現data.js/data.ets生命周期相關接口。DataAbility生命周期接口說明見下表。
    的頭像 發(fā)表于 06-20 09:39 ?372次閱讀