Spring bean的生命流程

Spring 是一個輕量級的 J2EE 開源框架,其目標是下降企業級應用開發難度,提升企業級應用開發效率。在日程開發中,咱們會常用 Spring 框架去構建應用。因此做爲一個常用的框架,瞭解其原理仍是頗有必要的。接下來咱們就從宏觀層面上,來看看 Spring 中的 bean 由實例化到銷燬的過程。在詳細討論 bean 生命週期前,先上一張圖,後面也會圍繞這張圖展開討論。java

bean實例化過程

圖1 bean實例化過程git

接下來對照上圖,一步一步對 singleton 類型 bean 的生命週期進行解析:github

  1. 實例化 bean 對象,相似於 new XXObject()
  2. 將配置文件中配置的屬性填充到剛剛建立的 bean 對象中。
  3. 檢查 bean 對象是否實現了 Aware 一類的接口,若是實現了則把相應的依賴設置到 bean 對象中。好比若是 bean 實現了 BeanFactoryAware 接口,Spring 容器在實例化bean的過程當中,會將 BeanFactory 容器注入到 bean 中。
  4. 調用 BeanPostProcessor 前置處理方法,即 postProcessBeforeInitialization(Object bean, String beanName)。
  5. 檢查 bean 對象是否實現了 InitializingBean 接口,若是實現,則調用 afterPropertiesSet 方法。或者檢查配置文件中是否配置了 init-method 屬性,若是配置了,則去調用 init-method 屬性配置的方法。
  6. 調用 BeanPostProcessor 後置處理方法,即 postProcessAfterInitialization(Object bean, String beanName)。咱們所熟知的 AOP 就是在這裏將 Adivce 邏輯織入到 bean 中的。
  7. 註冊 Destruction 相關回調方法。
  8. bean 對象處於就緒狀態,可使用了。
  9. 應用上下文被銷燬,調用註冊的 Destruction 相關方法。若是 bean 實現了 DispostbleBean 接口,Spring 容器會調用 destroy 方法。若是在配置文件中配置了 destroy 屬性,Spring 容器則會調用 destroy 屬性對應的方法。

上述流程從宏觀上對 Spring 中 singleton 類型 bean 的生命週期進行了描述,接下來講說所上面流程中的一些細節問題。
先看流程中的第二步 -- 設置對象屬性。在這一步中,對於普通類型的屬性,例如 String,Integer等,比較容易處理,直接設置便可。可是若是某個 bean 對象依賴另外一個 bean 對象,此時就不能直接設置了。Spring 容器首先要先去實例化 bean 依賴的對象,實例化好後才能設置到當前 bean 中。大體流程以下:
spring

圖2 依賴實例化流程圖數組

上面圖片描述的依賴比較簡單,就是 BeanA 依賴 BeanB。如今考慮這樣一種狀況,BeanA 依賴 BeanB,BeanB 依賴 BeanC,BeanC 又依賴 BeanA。三者造成了循環依賴,以下所示:框架

圖3 循環依賴post

對於這樣的循環依賴,根據依賴注入方式的不一樣,Spring 處理方式也不一樣。若是依賴靠構造器方式注入,則沒法處理,Spring 直接會報循環依賴異常。這個理解起來也不復雜,構造 BeanA 時須要 BeanB 做爲構造器參數,此時 Spring 容器會先實例化 BeanB。構造 BeanB 時,BeanB 又須要 BeanC 做爲構造器參數,Spring 容器又不得不先去構造 BeanC。最後構造 BeanC 時,BeanC 又依賴 BeanA 才能完成構造。此時,BeanA 還沒構造完成,BeanA 要等 BeanB 實例化好才能完成構造,BeanB 又要等 BeanC,BeanC 等 BeanA。這樣就造成了死循環,因此對於以構造器注入方式的循環依賴是無解的,Spring 容器會直接報異常。對於 setter 類型注入的循環依賴則能夠順利完成實例化並依次注入,這裏具體細節就不說了,詳細能夠參考《Spring源碼深度解析》一書相關章節。spa

循環依賴問題說完,接下來 bean 實例化流程中的第6步 -- 調用 BeanPostProcessor 後置處理方法。先介紹一下 BeanPostProcessor 接口,BeanPostProcessor 接口中包含了兩個方法,其定義以下:代理

public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception;

    Object postProcessAfterInitialization(Object bean, String beanName) throws Exception;
}

BeanPostProcessor 是一個頗有用的接口,經過實現接口咱們就能夠插手 bean 的實例化過程,爲拓展提供了可能。咱們所熟知的 AOP 就是在這裏進行織如入,具體點說是在 postProcessAfterInitialization(Object bean, String beanName) 執行織入邏輯的。下面就來講說 Spring AOP 織入的流程,以及 AOP 是怎樣和 IOC 整合的。先說 Spring AOP 織入流程,大體以下:code

  1. 查找實現了 PointcutAdvisor 類型的切面類,切面類包含了 Pointcut 和 Advice 實現類對象。
  2. 檢查 Pointcut 中的表達式是否能匹配當前 bean 對象。
  3. 若是匹配到了,代表應該對此對象織入 Advice。
  4. 將 bean,bean class對象,bean實現的interface的數組,Advice對象傳給代理工廠 ProxyFactory。代理工廠建立出 AopProxy 實現類,最後由 AopProxy 實現類建立 bean 的代理類,並將這個代理類返回。此時從 postProcessAfterInitialization(Object bean, String beanName) 返回的 bean 此時就不是原來的 bean 了,而是 bean 的代理類。原 bean 就這樣被無感的替換掉了,是否是有點偷天換柱的感受。

你們如今應該知道 AOP 是怎樣做用在 bean 上的了,那麼 AOP 是怎樣和 IOC 整合起來並協同工做的呢?下面就來簡單說一下。

Spring AOP 生成代理類的邏輯是在 AbstractAutoProxyCreator 相關子類中實現的,好比 DefaultAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator 等。上面說了 BeanPostProcessor 爲拓展留下了可能,這裏 AbstractAutoProxyCreator 就將可能變爲了現實。AbstractAutoProxyCreator 實現了 BeanPostProcessor 接口,這樣 AbstractAutoProxyCreator 能夠在 bean 初始化時作一些事情。光繼承這個接口還不夠,繼承這個接口只能獲取 bean,要想讓 AOP 生效,還須要拿到切面對象(包含 Pointcut 和 Advice)才行。因此 AbstractAutoProxyCreator 同時繼承了 BeanFactoryAware 接口,經過實現該接口,AbstractAutoProxyCreator 子類就可拿到 BeanFactory,有了 BeanFactory,就能夠獲取 BeanFactory 中全部的切面對象了。有了目標對象 bean,全部的切面類,此時就能夠爲 bean 生成代理對象了。

AbstractAutoProxyCreator繼承圖

圖4 AbstractAutoProxyCreator繼承圖(刪掉了一些不關心的繼承分支)

到這裏,從宏觀上已經對 bean 的生命流程進行了較爲詳細的描述。因爲暫時能力有限,只能從宏觀上分析,之前嘗試過去看 Spring IOC 的實現代碼,感受仍是太複雜了,細節太多,跟蹤了十幾二十個方法後就開始凌亂了。在幾回失敗的嘗試後,終於放棄了。後來總結了一下失敗的緣由,當時本身剛工做不是好久,代碼寫的少,經驗不足。而且在對 Spring 不少特性不熟悉的狀況下就去看 Spring 源碼,結果只能處處碰壁,陷入 Spring 各類細節之中久久不能自拔?。因此對於想看某個框架代碼的同窗,必定要在熟練使用這個框架的基礎上再去看。不要像我這樣急於求成,否則到最後只能失敗啊。本人這篇博客創建在仿寫了 Spring IOC 和 AOP的基礎上寫出來的,在仿寫過程當中參考了黃億華前輩的 tiny-spring 項目,有興趣的同窗能夠讀讀 tiny-spring。我本身仿寫的項目也放在了github上,傳送門 --> toy-spring

本篇博客到此結束,若是有寫錯的地方,歡迎指出來,謝謝!若是錯誤的地方對你形成了困擾,我表示很抱歉。

參考

本文在知識共享許可協議 4.0 下發布,轉載請註明出處
做者:coolblog
爲了得到更好的分類閱讀體驗,
請移步至本人的我的博客: http://www.coolblog.xyz

cc
本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

相關文章
相關標籤/搜索