1. 程式人生 > >如果你每次面試前都要去背一篇Spring中Bean的生命週期,請看完這篇文章

如果你每次面試前都要去背一篇Spring中Bean的生命週期,請看完這篇文章

# 前言 當你準備去複習Spring中Bean的生命週期的時候,這個時候你開始上網找資料,很大概率會看到下面這張圖: ![image-20200707094637369](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200707094637369.png) 先不論這張圖上是否全面,但是就說這張圖吧,你是不是背了又忘,忘了又背? 究其原因在於,你沒有理解為什麼需要這些步驟,也不知道為什麼要按這個順序執行 筆者在閱讀完整個`IOC`跟`AOP`的原始碼後,希望通過這篇文章講一講我的Spring中Bean生命週期的看法,幫助大家能理解性的記憶整個流程,而不是死記硬背! # 基礎知識補充 所謂理解也是建立在有一定知識儲備的基礎上的,所以這裡先補充一些基礎概念 ## Bean建立的三個階段 Spring在建立一個Bean時是分為三個步驟的 - 例項化,可以理解為new一個物件 - 屬性注入,可以理解為呼叫setter方法完成屬性注入 - 初始化,你可以按照Spring的規則配置一些初始化的方法(例如,`@PostConstruct`註解) ## 生命週期的概念 Bean的生命週期指的就是在上面三個步驟中後置處理器`BeanPostprocessor`穿插執行的過程 ## 後置處理器的分析 按照實現介面進行分類 1. 直接實現了`BeanPostProcessor`介面 最簡單的後置處理器,也就是說直接實現了`BeanPostProcessor`介面,這種後置處理器只能在初始化前後執行 ```java 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; } } ``` 2. 直接實現了`InstantiationAwareBeanPostProcessor`介面 在第一種後置處理的基礎上進行了一層擴充套件,可以在Bean的例項化階段前後執行 ```java // 繼承了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; } } ``` 3. Spring內部專用的後置處理器 可能有的小夥伴認為,第三種後置處理器肯定就是用來在屬性注入前後執行了的吧。我只能說,大兄弟,太天真了,看看下面這張圖 ![image-20200707163736980](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200707163736980.png) 這種情況下再為屬性注入階段專門提供兩個方法是不是有點多餘呢?實際上第三種後置處理器是Spring為了自己使用而專門設計的 ```java 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; } } ``` 一般我們在探究生命週期的時候都不會考慮這種後置處理器的執行 # 生命週期詳細介紹 在瞭解了上面的概念後,我們再來看看這張圖 ![image-20200707094637369](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200707094637369.png) 至少現在這張圖上缺少了例項化前後後置處理器的執行流程,對吧? 再補充上這一點之後,我們再來看看,屬性注入後緊接著已經是初始化的階段,在初始化階段開始前應該要呼叫`BeanPostProcessor`的預初始化方法(`postProcessBeforeInitialization`),然後呼叫自定義的初始化方法,最後呼叫`postProcessAfterInitialization`,這是沒有問題,但是為什麼要在初始前還要呼叫Aware介面的方法,如果你看了原始碼的話可能會說,原始碼就是這麼寫的,別人就是這麼設計的,但是為什麼要這麼設計呢?**我們看原始碼到一定階段後不應該僅僅停留在是什麼的階段,而應該多思考為什麼,這樣能幫助你更好的瞭解這個框架** 那麼為什麼Aware介面非要在初始化前執行呢? 這樣做的目的是因為,初始化可能會依賴Aware介面提供的狀態,例如下面這個例子 ```java @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`方法的開銷大還是執行配置檔案中指定名稱的初始化方法開銷大呢?我們不妨用虛擬碼演示下 ```java // afterPropertiesSet,強轉後直接呼叫 ((InitializingBean) bean).afterPropertiesSet() // 反射呼叫init-method方法 // 第一步:找到這個方法 Method method = class.getMethod(methodName) // 第二步:反射呼叫這個方法 method.invoke(bean,null) ``` 相比而言肯定是第一種的效率高於第二種,一個只是做了一次方法呼叫,而另外一個要呼叫兩次反射。 因此,`afterPropertiesSet`的優先順序高於XML配置的方式 所以,這三個方法的執行順序為: 1. `@PostConstruct`註解標註的方法 2. 實現了`InitializingBean`介面後複寫的`afterPropertiesSet`方法 3. XML中自定義的初始化方法 在完成初始化,沒什麼好說的了,最後呼叫一下`postProcessAfterInitialization`,整個Bean的生命週期到此結束 # 總結 本文的主要目的是想要幫助大家更好的理解整個Bean的生命週期,不過理解是建立在有一定知識儲存的基礎上的 你至少要對Bean的後置處理器跟Bean建立有一個大概的理解,那麼通過本文你能理清一些細節方面的東西 例如,為什麼Aware介面執行在初始化階段之前?為什麼初始化的三個方法會按 `@PostConstruct`,`afterPropertiesSet`,XML中定義的初始化方法這個順序執行。 本文也將是我整個Spring關於`IOC`跟`AOP`的最後一篇文字,在這之後我打算做一個Spring事務專題,預計6到7篇文章,事務結束後關於整個Spring原始碼的學習也就結束啦!本來預期要一年才能完成,不過因為最近離職了,所以打算全職在家寫完這個系列! 如果本文對你由幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜尋:程式設計師DMZ,或者掃描下方二維碼,跟著我一起認認真真學Java,踏踏實實做一個coder。 ![公眾號](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci8lRTUlODUlQUMlRTQlQkMlOTclRTUlOEYlQjcuanBn?x-oss-process=image/format,png) 我叫DMZ,一個在學習路上匍匐前行的小菜鳥!