當你準備去複習Spring中Bean的生命週期的時候,這個時候你開始上網找資料,很大機率會看到下面這張圖:程序員
先不論這張圖上是否全面,可是就說這張圖吧,你是否是背了又忘,忘了又背?web
究其緣由在於,你沒有理解爲何須要這些步驟,也不知道爲何要按這個順序執行微信
筆者在閱讀完整個IOC
跟AOP
的源碼後,但願經過這篇文章講一講個人Spring中Bean生命週期的見解,幫助你們能理解性的記憶整個流程,而不是死記硬背!app
所謂理解也是創建在有必定知識儲備的基礎上的,因此這裏先補充一些基礎概念框架
Spring在建立一個Bean時是分爲三個步驟的編輯器
@PostConstruct
註解)
Bean的生命週期指的就是在上面三個步驟中後置處理器BeanPostprocessor
穿插執行的過程ide
按照實現接口進行分類函數
BeanPostProcessor
接口
最簡單的後置處理器,也就是說直接實現了BeanPostProcessor
接口,這種後置處理器只能在初始化先後執行post
public interface BeanPostProcessor {
// 初始化前執行的方法 @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } // 初始化後執行的方法 default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } 複製代碼
InstantiationAwareBeanPostProcessor
接口
在第一種後置處理的基礎上進行了一層擴展,能夠在Bean的實例化階段先後執行學習
// 繼承了BeanPostProcessor,額外提供了兩個方法用於在實例化先後的階段執行
// 由於實例化後緊接着就要進行屬性注入,因此這個接口中還提供了一個屬性注入的方法 public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { // 實例化前執行 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爲了本身使用而專門設計的
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
// 推測bean的類型,例如在屬性注入階段咱們就須要知道符合依賴類型的Bean有哪些 @Nullable default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException { return null; } // 推斷出全部符合要求的構造函數,在實例化對象的時候咱們就須要明確到底使用哪一個構造函數 @Nullable default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException { return null; } // 獲取一個提早暴露的對象,用於解決循環依賴 default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; } } 複製代碼
通常咱們在探究生命週期的時候都不會考慮這種後置處理器的執行
在瞭解了上面的概念後,咱們再來看看這張圖
至少如今這張圖上缺乏了實例化先後後置處理器的執行流程,對吧?
再補充上這一點以後,咱們再來看看,屬性注入後緊接着已是初始化的階段,在初始化階段開始前應該要調用BeanPostProcessor
的預初始化方法(postProcessBeforeInitialization
),而後調用自定義的初始化方法,最後調用postProcessAfterInitialization
,這是沒有問題,可是爲何要在初始前還要調用Aware接口的方法,若是你看了源碼的話可能會說,源碼就是這麼寫的,別人就是這麼設計的,可是爲何要這麼設計呢?咱們看源碼到必定階段後不該該僅僅停留在是什麼的階段,而應該多思考爲何,這樣能幫助你更好的瞭解這個框架
那麼爲何Aware接口非要在初始化前執行呢?
這樣作的目的是由於,初始化可能會依賴Aware接口提供的狀態,例以下面這個例子
@Component
public class A implements InitializingBean, ApplicationContextAware { ApplicationContext applicationContext; @Override public void afterPropertiesSet() throws Exception { // 初始化方法須要用到ApplicationContextAware提供的ApplicationContext System.out.println(applicationContext); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } 複製代碼
這種狀況下Aware接口固然要在初始化前執行啦!
另外,在討論Bean的初始化的時候常常會碰到下面這個問題,@PostConstruct
,afterPropertiesSet
跟XML中配置的init-method
方法的執行順序。
請注意,@PostConstruct
其實是在postProcessBeforeInitialization
方法中處理的,嚴格來講它不屬於初始化階段調用的方法,因此這個方法是最早調用的
其次咱們思考下是調用afterPropertiesSet
方法的開銷大仍是執行配置文件中指定名稱的初始化方法開銷大呢?咱們不妨用僞代碼演示下
// afterPropertiesSet,強轉後直接調用
((InitializingBean) bean).afterPropertiesSet() // 反射調用init-method方法 // 第一步:找到這個方法 Method method = class.getMethod(methodName) // 第二步:反射調用這個方法 method.invoke(bean,null) 複製代碼
相比而言確定是第一種的效率高於第二種,一個只是作了一次方法調用,而另一個要調用兩次反射。
所以,afterPropertiesSet
的優先級高於XML配置的方式
因此,這三個方法的執行順序爲:
@PostConstruct
註解標註的方法
InitializingBean
接口後複寫的
afterPropertiesSet
方法
在完成初始化,沒什麼好說的了,最後調用一下postProcessAfterInitialization
,整個Bean的生命週期到此結束
本文的主要目的是想要幫助你們更好的理解整個Bean的生命週期,不過理解是創建在有必定知識存儲的基礎上的
你至少要對Bean的後置處理器跟Bean建立有一個大概的理解,那麼經過本文你能理清一些細節方面的東西
例如,爲何Aware接口執行在初始化階段以前?爲何初始化的三個方法會按
@PostConstruct
,afterPropertiesSet
,XML中定義的初始化方法這個順序執行。
本文也將是我整個Spring關於IOC
跟AOP
的最後一篇文字,在這以後我打算作一個Spring事務專題,預計6到7篇文章,事務結束後關於整個Spring源碼的學習也就結束啦!原本預期要一年才能完成,不過由於最近離職了,因此打算全職在家寫完這個系列!
若是本文對你由幫助的話,記得點個贊吧!也歡迎關注個人公衆號,微信搜索:程序員DMZ,或者掃描下方二維碼,跟着我一塊兒認認真真學Java,踏踏實實作一個coder。
我叫DMZ,一個在學習路上匍匐前行的小菜鳥!
本文使用 mdnice 排版