前言
當你準備去複習Spring中Bean的生命週期的時候,這個時候你開始上網找資料,很大概率會看到下面這張圖:
先不論這張圖上是否全面,但是就說這張圖吧,你是不是背了又忘,忘了又背?
究其原因在於,你沒有理解為什麼需要這些步驟,也不知道為什麼要按這個順序執行
筆者在閱讀完整個IOC
跟AOP
的原始碼後,希望通過這篇文章講一講我的Spring中Bean生命週期的看法,幫助大家能理解性的記憶整個流程,而不是死記硬背!
基礎知識補充
所謂理解也是建立在有一定知識儲備的基礎上的,所以這裡先補充一些基礎概念
Bean建立的三個階段
Spring在建立一個Bean時是分為三個步驟的
- 例項化,可以理解為new一個物件
- 屬性注入,可以理解為呼叫setter方法完成屬性注入
- 初始化,你可以按照Spring的規則配置一些初始化的方法(例如,
@PostConstruct
註解)
生命週期的概念
Bean的生命週期指的就是在上面三個步驟中後置處理器BeanPostprocessor
穿插執行的過程
後置處理器的分析
按照實現介面進行分類
- 直接實現了
BeanPostProcessor
介面
最簡單的後置處理器,也就是說直接實現了BeanPostProcessor
介面,這種後置處理器只能在初始化前後執行
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內部專用的後置處理器
可能有的小夥伴認為,第三種後置處理器肯定就是用來在屬性注入前後執行了的吧。我只能說,大兄弟,太天真了,看看下面這張圖
這種情況下再為屬性注入階段專門提供兩個方法是不是有點多餘呢?實際上第三種後置處理器是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
方法 - XML中自定義的初始化方法
在完成初始化,沒什麼好說的了,最後呼叫一下postProcessAfterInitialization
,整個Bean的生命週期到此結束
總結
本文的主要目的是想要幫助大家更好的理解整個Bean的生命週期,不過理解是建立在有一定知識儲存的基礎上的
你至少要對Bean的後置處理器跟Bean建立有一個大概的理解,那麼通過本文你能理清一些細節方面的東西
例如,為什麼Aware介面執行在初始化階段之前?為什麼初始化的三個方法會按
@PostConstruct
,afterPropertiesSet
,XML中定義的初始化方法這個順序執行。
本文也將是我整個Spring關於IOC
跟AOP
的最後一篇文字,在這之後我打算做一個Spring事務專題,預計6到7篇文章,事務結束後關於整個Spring原始碼的學習也就結束啦!本來預期要一年才能完成,不過因為最近離職了,所以打算全職在家寫完這個系列!
如果本文對你由幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜尋:程式設計師DMZ,或者掃描下方二維碼,跟著我一起認認真真學Java,踏踏實實做一個coder。
我叫DMZ,一個在學習路上匍匐前行的小菜鳥!