1. 基礎(chǔ)知識(shí)
1.1 什么是 IoC ?
1.2 Bean 生命周期
1.3 執(zhí)行流程
1.4 擴(kuò)展方法
2. 源碼解讀
2.1 代碼入口
2.2 實(shí)例化
2.3 屬性賦值
2.4 初始化
2.5 銷(xiāo)毀
3. 寫(xiě)在最后
Spring Bean 的生命周期,面試時(shí)非常容易問(wèn),這不,前段時(shí)間就有個(gè)讀者去面試,因?yàn)椴粫?huì)回答這個(gè)問(wèn)題,一面都沒(méi)有過(guò)。
如果只講基礎(chǔ)知識(shí),感覺(jué)和網(wǎng)上大多數(shù)文章沒(méi)有區(qū)別,但是我又想寫(xiě)得稍微深入一點(diǎn)。
考慮很多同學(xué)不喜歡看源碼,我就把文章分為 2 大部分,前面是基礎(chǔ)知識(shí),主要方便大家面試和學(xué)習(xí) ,后面是源碼部分,對(duì)源碼感興趣的同學(xué)可以繼續(xù)往后面看。
不 BB,上文章目錄。
1. 基礎(chǔ)知識(shí)
1.1 什么是 IoC ?
IoC,控制反轉(zhuǎn),想必大家都知道,所謂的控制反轉(zhuǎn),就是把 new 對(duì)象的權(quán)利交給容器,所有的對(duì)象都被容器控制,這就叫所謂的控制反轉(zhuǎn)。
IoC 很好地體現(xiàn)了面向?qū)ο笤O(shè)計(jì)法則之一 —— 好萊塢法則:“別找我們,我們找你 ”,即由 IoC 容器幫對(duì)象找相應(yīng)的依賴(lài)對(duì)象并注入,而不是由對(duì)象主動(dòng)去找。
理解好 IoC 的關(guān)鍵是要明確 “誰(shuí)控制誰(shuí),控制什么,為何是反轉(zhuǎn)(有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了),哪些方面反轉(zhuǎn)了”。
誰(shuí)控制誰(shuí),控制什么?
傳統(tǒng) Java SE 程序設(shè)計(jì),我們直接在對(duì)象內(nèi)部通過(guò) new 進(jìn)行創(chuàng)建對(duì)象,是程序主動(dòng)去創(chuàng)建依賴(lài)對(duì)象。而 IoC 是由專(zhuān)門(mén)一個(gè)容器來(lái)創(chuàng)建這些對(duì)象,即由 IoC 容器來(lái)控制對(duì)象的創(chuàng)建。
誰(shuí)控制誰(shuí)?當(dāng)然是 IoC 容器控制了對(duì)象;
控制什么?主要控制了外部資源獲取(不只是對(duì)象,比如包括文件等)。
為何是反轉(zhuǎn),哪些方面反轉(zhuǎn)了?
有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對(duì)象中主動(dòng)控制去直接獲取依賴(lài)對(duì)象,也就是正轉(zhuǎn),而反轉(zhuǎn)則是由容器來(lái)幫忙創(chuàng)建及注入依賴(lài)對(duì)象。
為何是反轉(zhuǎn)?因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾?lài)對(duì)象,對(duì)象只是被動(dòng)的接受依賴(lài)對(duì)象,所以是反轉(zhuǎn);
哪些方面反轉(zhuǎn)了?依賴(lài)對(duì)象的獲取被反轉(zhuǎn)了。
1.2 Bean 生命周期
對(duì) Prototype Bean 來(lái)說(shuō),當(dāng)用戶(hù) getBean 獲得 Prototype Bean 的實(shí)例后,IOC 容器就不再對(duì)當(dāng)前實(shí)例進(jìn)行管理,而是把管理權(quán)交由用戶(hù),此后再 getBean 生成的是新的實(shí)例。
所以我們描述 Bean 的生命周期,都是指的 Singleton Bean。
Bean 生命周期過(guò)程:
實(shí)例化 :第 1 步,實(shí)例化一個(gè) Bean 對(duì)象;
屬性賦值 :第 2 步,為 Bean 設(shè)置相關(guān)屬性和依賴(lài);
初始化 :初始化的階段的步驟比較多,5、6 步是真正的初始化,第 3、4 步為在初始化前執(zhí)行,第 7 步在初始化后執(zhí)行,初始化完成之后,Bean 就可以被使用了;
銷(xiāo)毀 :第 8~10 步,第 8 步其實(shí)也可以算到銷(xiāo)毀階段,但不是真正意義上的銷(xiāo)毀,而是先在使用前注冊(cè)了銷(xiāo)毀的相關(guān)調(diào)用接口,為了后面第 9、10 步真正銷(xiāo)毀 Bean 時(shí)再執(zhí)行相應(yīng)的方法。
整個(gè)執(zhí)行流程稍微有些抽象,下面我們通過(guò)代碼,來(lái)演示執(zhí)行流程。
1.3 執(zhí)行流程
創(chuàng)建一個(gè) LouzaiBean。
publicclassLouzaiBeanimplementsInitializingBean,BeanFactoryAware,BeanNameAware,DisposableBean{ /** *姓名 */ privateStringname; publicLouzaiBean(){ System.out.println("1.調(diào)用構(gòu)造方法:我出生了!"); } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; System.out.println("2.設(shè)置屬性:我的名字叫"+name); } @Override publicvoidsetBeanName(Strings){ System.out.println("3.調(diào)用BeanNameAware#setBeanName方法:我要上學(xué)了,起了個(gè)學(xué)名"); } @Override publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{ System.out.println("4.調(diào)用BeanFactoryAware#setBeanFactory方法:選好學(xué)校了"); } @Override publicvoidafterPropertiesSet()throwsException{ System.out.println("6.InitializingBean#afterPropertiesSet方法:入學(xué)登記"); } publicvoidinit(){ System.out.println("7.自定義init方法:努力上學(xué)ing"); } @Override publicvoiddestroy()throwsException{ System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了"); } publicvoiddestroyMethod(){ System.out.println("10.自定義destroy方法:睡了,別想叫醒我"); } publicvoidwork(){ System.out.println("Bean使用中:工作,只有對(duì)社會(huì)沒(méi)有用的人才放假。。"); } }
自定義一個(gè)后處理器 MyBeanPostProcessor。
publicclassMyBeanPostProcessorimplementsBeanPostProcessor{ @Override publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{ System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到學(xué)校報(bào)名啦"); returnbean; } @Override publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{ System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:終于畢業(yè),拿到畢業(yè)證啦!"); returnbean; } }
applicationContext.xml 配置文件(部分)。
測(cè)試入口:
publicclassMyTest{ publicstaticvoidmain(String[]args){ ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml"); LouzaiBeanlouzaiBean=(LouzaiBean)context.getBean("louzaiBean"); louzaiBean.work(); ((ClassPathXmlApplicationContext)context).destroy(); } }
執(zhí)行結(jié)果:
1.調(diào)用構(gòu)造方法:我出生了! 2.設(shè)置屬性:我的名字叫樓仔 3.調(diào)用BeanNameAware#setBeanName方法:我要上學(xué)了,起了個(gè)學(xué)名 4.調(diào)用BeanFactoryAware#setBeanFactory方法:選好學(xué)校了 5.BeanPostProcessor.postProcessBeforeInitialization方法:到學(xué)校報(bào)名啦 6.InitializingBean#afterPropertiesSet方法:入學(xué)登記 7.自定義init方法:努力上學(xué)ing 8.BeanPostProcessor#postProcessAfterInitialization方法:終于畢業(yè),拿到畢業(yè)證啦! Bean使用中:工作,只有對(duì)社會(huì)沒(méi)有用的人才放假。。 9.DisposableBean#destroy方法:平淡的一生落幕了 10.自定義destroy方法:睡了,別想叫醒我
這個(gè)流程非常清晰,Bean 生命周期流程圖能完全對(duì)應(yīng)起來(lái)。
1.4 擴(kuò)展方法
我們發(fā)現(xiàn),整個(gè)生命周期有很多擴(kuò)展過(guò)程,大致可以分為 4 類(lèi):
Aware 接口:讓 Bean 能拿到容器的一些資源,例如 BeanNameAware 的 setBeanName() ,BeanFactoryAware 的 setBeanFactory() ;
后處理器:進(jìn)行一些前置和后置的處理,例如 BeanPostProcessor 的 postProcessBeforeInitialization() 和 postProcessAfterInitialization() ;
生命周期接口:定義初始化方法和銷(xiāo)毀方法的,例如 InitializingBean 的 afterPropertiesSet() ,以及 DisposableBean 的 destroy() ;
配置生命周期方法:可以通過(guò)配置文件,自定義初始化和銷(xiāo)毀方法,例如配置文件配置的 init() 和 destroyMethod() 。
2. 源碼解讀
注意:Spring 的版本是 5.2.15.RELEASE ,否則和我的代碼不一樣!!!
上面的知識(shí),網(wǎng)上其實(shí)都有,下面才是我們的重頭戲,讓你跟著我走一遍代碼流程。
2.1 代碼入口
這里需要多跑幾次,把前面的 beanName 跳過(guò)去,只看 louzaiBean。
進(jìn)入 doGetBean(),從 getSingleton() 沒(méi)有找到對(duì)象,進(jìn)入創(chuàng)建 Bean 的邏輯。
2.2 實(shí)例化
進(jìn)入 doCreateBean() 后,調(diào)用 createBeanInstance()。
進(jìn)入 createBeanInstance() 后,調(diào)用 instantiateBean()。
走進(jìn)示例 LouzaiBean 的方法,實(shí)例化 LouzaiBean。
圖片
2.3 屬性賦值
再回到 doCreateBean(),繼續(xù)往后走,進(jìn)入 populateBean()。
這個(gè)方法非常重要,里面其實(shí)就是依賴(lài)注入的邏輯,不過(guò)這個(gè)不是我們今天的重點(diǎn),大家如果對(duì)依賴(lài)注入和循環(huán)依賴(lài)感興趣,可以翻閱我之前的文章。
進(jìn)入 populateBean() 后,執(zhí)行 applyPropertyValues()
進(jìn)入 applyPropertyValues(),執(zhí)行 bw.setPropertyValues()
進(jìn)入 processLocalProperty(),執(zhí)行 ph.setValue()。
走進(jìn)示例 LouzaiBean 的方法,給 LouzaiBean 賦值 name。
到這里,populateBean() 就執(zhí)行完畢,下面開(kāi)始初始化 Bean。
2.4 初始化
我們繼續(xù)回到 doCreateBean(),往后執(zhí)行 initializeBean()。
走進(jìn)示例 LouzaiBean 的方法,給 LouzaiBean 設(shè)置 BeanName。
回到 invokeAwareMethods()。
走進(jìn)示例 LouzaiBean 的方法,給 LouzaiBean 設(shè)置 BeanFactory。
第一次回到 initializeBean() ,執(zhí)行下面邏輯。
這里需要多循環(huán)幾次,找到 MyBeanPostProcessor 的策略方法。
我們自己定義的后置處理方法。
第二次回到 initializeBean() ,執(zhí)行下面邏輯。
走進(jìn)示例 LouzaiBean 的方法,執(zhí)行 afterPropertiesSet()。
返回 invokeInitMethods(),執(zhí)行下面邏輯。
進(jìn)入 invokeCustomInitMethod(),執(zhí)行下面邏輯。
走進(jìn)示例 LouzaiBean 的方法,執(zhí)行 init()。
第三次回到 initializeBean() ,執(zhí)行下面邏輯。
我們自己定義的后置處理方法。
到這里,初始化的流程全部結(jié)束,都是圍繞 initializeBean() 展開(kāi)。
2.5 銷(xiāo)毀
當(dāng) louzaiBean 生成后,后面開(kāi)始執(zhí)行銷(xiāo)毀操作,整個(gè)流程就比較簡(jiǎn)單。
走進(jìn)示例 LouzaiBean 的方法,執(zhí)行 destroy()。
回到 destroy(),執(zhí)行下面邏輯。
走進(jìn)示例 LouzaiBean 的方法,執(zhí)行 destroyMethod()。
到這里,所有的流程全部結(jié)束,文章詳細(xì)描述所有的代碼邏輯流轉(zhuǎn),你可以完全根據(jù)上面的邏輯,自己 debug 一遍。
3. 寫(xiě)在最后
我們?cè)倩仡櫼幌聨讉€(gè)重要的方法:
doCreateBean() :這個(gè)是入口;
createBeanInstance() :用來(lái)初始化 Bean,里面會(huì)調(diào)用對(duì)象的構(gòu)造方法;
populateBean() :屬性對(duì)象的依賴(lài)注入,以及成員變量初始化;
initializeBean() :里面有 4 個(gè)方法,
先執(zhí)行 aware 的 BeanNameAware、BeanFactoryAware 接口;
再執(zhí)行 BeanPostProcessor 前置接口;
然后執(zhí)行 InitializingBean 接口,以及配置的 init();
最后執(zhí)行 BeanPostProcessor 的后置接口。
destory() :先執(zhí)行 DisposableBean 接口,再執(zhí)行配置的 destroyMethod()。
對(duì)于 populateBean(),里面的核心其實(shí)是對(duì)象的依賴(lài)注入,這里也是常考的知識(shí)點(diǎn),比如循環(huán)依賴(lài),大家如果對(duì)這塊也感興趣,可以和我交流。
-
JAVA
+關(guān)注
關(guān)注
19文章
2966瀏覽量
104702 -
編程
+關(guān)注
關(guān)注
88文章
3614瀏覽量
93686 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14338 -
IOC
+關(guān)注
關(guān)注
0文章
28瀏覽量
10099
原文標(biāo)題:阿里云面試:Spring 中 Bean 的生命周期是怎樣的?
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論