javascript
如果你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章
前言
當你準備去復習Spring中Bean的生命周期的時候,這個時候你開始上網(wǎng)找資料,很大概率會看到下面這張圖:
先不論這張圖上是否全面,但是就說這張圖吧,你是不是背了又忘,忘了又背?
究其原因在于,你沒有理解為什么需要這些步驟,也不知道為什么要按這個順序執(zhí)行
筆者在閱讀完整個IOC跟AOP的源碼后,希望通過這篇文章講一講我的Spring中Bean生命周期的看法,幫助大家能理解性的記憶整個流程,而不是死記硬背!
基礎(chǔ)知識補充
所謂理解也是建立在有一定知識儲備的基礎(chǔ)上的,所以這里先補充一些基礎(chǔ)概念
Bean創(chuàng)建的三個階段
Spring在創(chuàng)建一個Bean時是分為三個步驟的
實例化,可以理解為new一個對象
屬性注入,可以理解為調(diào)用setter方法完成屬性注入
初始化,你可以按照Spring的規(guī)則配置一些初始化的方法(例如,@PostConstruct注解)
生命周期的概念
Bean的生命周期指的就是在上面三個步驟中后置處理器BeanPostprocessor穿插執(zhí)行的過程
后置處理器的分析
按照實現(xiàn)接口進行分類
直接實現(xiàn)了BeanPostProcessor接口
最簡單的后置處理器,也就是說直接實現(xiàn)了BeanPostProcessor接口,這種后置處理器只能在初始化前后執(zhí)行
public?interface?BeanPostProcessor?{//?初始化前執(zhí)行的方法@Nullabledefault?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{return?bean;}????//?初始化后執(zhí)行的方法default?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{return?bean;}}直接實現(xiàn)了InstantiationAwareBeanPostProcessor接口
在第一種后置處理的基礎(chǔ)上進行了一層擴展,可以在Bean的實例化階段前后執(zhí)行
//?繼承了BeanPostProcessor,額外提供了兩個方法用于在實例化前后的階段執(zhí)行 //?因為實例化后緊接著就要進行屬性注入,所以這個接口中還提供了一個屬性注入的方法 public?interface?InstantiationAwareBeanPostProcessor?extends?BeanPostProcessor?{//?實例化前執(zhí)行default?Object?postProcessBeforeInstantiation(Class<?>?beanClass,?String?beanName)?throws?BeansException?{return?null;}//?實例化后置default?boolean?postProcessAfterInstantiation(Object?bean,?String?beanName)?throws?BeansException?{return?true;}//?屬性注入default?PropertyValues?postProcessPropertyValues(PropertyValues?pvs,?PropertyDescriptor[]?pds,?Object?bean,?String?beanName)?throws?BeansException?{return?pvs;} }Spring內(nèi)部專用的后置處理器
可能有的小伙伴認為,第三種后置處理器肯定就是用來在屬性注入前后執(zhí)行了的吧。我只能說,大兄弟,太天真了,看看下面這張圖
這種情況下再為屬性注入階段專門提供兩個方法是不是有點多余呢?實際上第三種后置處理器是Spring為了自己使用而專門設(shè)計的
public?interface?SmartInstantiationAwareBeanPostProcessor?extends?InstantiationAwareBeanPostProcessor?{//?推測bean的類型,例如在屬性注入階段我們就需要知道符合依賴類型的Bean有哪些@Nullabledefault?Class<?>?predictBeanType(Class<?>?beanClass,?String?beanName)?throws?BeansException?{return?null;}//?推斷出所有符合要求的構(gòu)造函數(shù),在實例化對象的時候我們就需要明確到底使用哪個構(gòu)造函數(shù)@Nullabledefault?Constructor<?>[]?determineCandidateConstructors(Class<?>?beanClass,?String?beanName)throws?BeansException?{return?null;}//?獲取一個提前暴露的對象,用于解決循環(huán)依賴default?Object?getEarlyBeanReference(Object?bean,?String?beanName)?throws?BeansException?{return?bean;}}一般我們在探究生命周期的時候都不會考慮這種后置處理器的執(zhí)行
生命周期詳細介紹
在了解了上面的概念后,我們再來看看這張圖
至少現(xiàn)在這張圖上缺少了實例化前后后置處理器的執(zhí)行流程,對吧?
再補充上這一點之后,我們再來看看,屬性注入后緊接著已經(jīng)是初始化的階段,在初始化階段開始前應該要調(diào)用BeanPostProcessor的預初始化方法(postProcessBeforeInitialization),然后調(diào)用自定義的初始化方法,最后調(diào)用postProcessAfterInitialization
這是沒有問題,但是為什么要在初始前還要調(diào)用Aware接口的方法,如果你看了源碼的話可能會說,源碼就是這么寫的,別人就是這么設(shè)計的,但是為什么要這么設(shè)計呢?我們看源碼到一定階段后不應該僅僅停留在是什么的階段,而應該多思考為什么,這樣能幫助你更好的了解這個框架
那么為什么Aware接口非要在初始化前執(zhí)行呢?
這樣做的目的是因為,初始化可能會依賴Aware接口提供的狀態(tài),例如下面這個例子
@Component public?class?A?implements?InitializingBean,?ApplicationContextAware?{ApplicationContext?applicationContext;@Overridepublic?void?afterPropertiesSet()?throws?Exception?{//?初始化方法需要用到ApplicationContextAware提供的ApplicationContextSystem.out.println(applicationContext);}@Overridepublic?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{this.applicationContext?=?applicationContext;} }這種情況下Aware接口當然要在初始化前執(zhí)行啦!
另外,在討論Bean的初始化的時候經(jīng)常會碰到下面這個問題,@PostConstruct,afterPropertiesSet跟XML中配置的init-method方法的執(zhí)行順序。
請注意,@PostConstruct實際上是在postProcessBeforeInitialization方法中處理的,嚴格來說它不屬于初始化階段調(diào)用的方法,所以這個方法是最先調(diào)用的
其次我們思考下是調(diào)用afterPropertiesSet方法的開銷大還是執(zhí)行配置文件中指定名稱的初始化方法開銷大呢?我們不妨用偽代碼演示下
// afterPropertiesSet,強轉(zhuǎn)后直接調(diào)用 ((InitializingBean)?bean).afterPropertiesSet()//?反射調(diào)用init-method方法 //?第一步:找到這個方法 Method?method?=?class.getMethod(methodName) //?第二步:反射調(diào)用這個方法 method.invoke(bean,null)相比而言肯定是第一種的效率高于第二種,一個只是做了一次方法調(diào)用,而另外一個要調(diào)用兩次反射。
因此,afterPropertiesSet的優(yōu)先級高于XML配置的方式
所以,這三個方法的執(zhí)行順序為:
@PostConstruct注解標注的方法
實現(xiàn)了InitializingBean接口后復寫的afterPropertiesSet方法
XML中自定義的初始化方法
在完成初始化,沒什么好說的了,最后調(diào)用一下postProcessAfterInitialization,整個Bean的生命周期到此結(jié)束
??
總結(jié)
本文的主要目的是想要幫助大家更好的理解整個Bean的生命周期,不過理解是建立在有一定知識存儲的基礎(chǔ)上的
你至少要對Bean的后置處理器跟Bean創(chuàng)建有一個大概的理解,那么通過本文你能理清一些細節(jié)方面的東西
例如,為什么Aware接口執(zhí)行在初始化階段之前?為什么初始化的三個方法會按
@PostConstruct,afterPropertiesSet,XML中定義的初始化方法這個順序執(zhí)行。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的如果你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 北风设计模式课程---13、享元模式
- 下一篇: leetcode-654-最大二叉树