簡單談談Spring的IoC以及bean的生命周期


一、前言

  這幾天正在復習Spring的相關內容,同時想要對Spring的實現原理做一些深入的研究。今天看了看SpringIoC的實現,找到了一篇非常詳細的博客,研究了一個下午,看完之后唯一的感受就是——太復雜了。Spring源碼中,類和接口的體系非常的復雜,同時方法的實現也是,方法調用感覺無窮無盡,甚至相互調用,給我繞的暈暈的。應該是自己目前的技術水平還太低,項目經驗也不足,甚至對Spring的運用都不夠熟悉,所以研究源碼對我來說可能還是太早了。

  雖然對於SpringIoC,我可能連十分之一都還沒有弄懂,但是一個下午的研究也不是毫無收獲。這篇博客就來簡單講講我對IoC的理解,以及Spring中,IoC最基本的實現流程。


二、正文

2.1 什么是IoC

  在我們使用傳統的編碼方式編寫代碼時,如果在類中需要引用另外一個類的對象,我們一般會直接在類中使用new關鍵字,創建這樣一個對象。此時,使用這個對象的類,就與這個對象所對應的類產生了耦合性。

  IoC中文名稱為控制反轉,它就是用來解決上述問題的一種設計思想,它能指導我們設計出耦合性更低,更易於管理的代碼。傳統的程序,都是在需要使用的地方主動地創建對象,而IoC的思想卻是將創建對象的工作交給IoC容器去進行。我們告訴IoC容器,它需要創建那些對象,IoC容器創建好這些對象,然后主動地將它們注入到需要使用的地方。此時,需要使用對象的那些類,並不主動地獲取對象,而且由IoC容器為它們分配,控制權在IoC容器手上,這就是控制反轉。當然,IoC容器並不只是控制對象,可以理解為控制外部資源的獲取

  IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。


2.2 IoC和DI的關系

  DI中文名稱為依賴注入,它其實和IoC是相同的概念,或者可以理解為它是IoC的一種具體的實現方式。IoC的概念可能比較模糊,控制反轉只是一種思想,可能僅僅只是停留在由其他組件控制對象,而不是在使用的地方直接創建這一層面,但是具體如何實現並沒有指明。而DI就是它的一種實現思路,由容器創建對象,並主動將對象注入到需要使用它的地方。2.1中對IoC的解釋,實際上更加偏向於DI


2.3 Spring如何實現IoC

  這一塊,我就簡單地說一說我今天下午在研究SpringIoC源碼的過程中,了解到的一些內容。首先,SpringIoC容器,可以簡單地理解為就是BeanFactory接口的一個實現類對象,比如Spring的應用上下文接口ApplicationContext就是繼承自BeanFactory,而我們使用較多的ClassPathXmlApplicationContext就是ApplicationContext的一個實現類。它們都可以理解為是SpringIoC容器,而BeanFactory就是這個繼承體系中的最高層。下面我就以單例bean的創建,簡單地說一說SpringIoC實現的一個過程,假設使用到的是ClassPathXmlApplicationContext這個容器,解析的是xml配置文件:

  1. 容器解析xml配置文件,將聲明在xml文件中的bean的配置信息提取出來,每一個bean的配置信息被封裝成一個BeanDefinitionHolder對象。BeanDefinitionHolder主要包含三個成員——BeanDefinition對象,bean的名稱(id),以及bean的別名。BeanDefinition對象就是用來保存一個bean的配置信息,比如bean的作用域,bean的類型,bean的依賴,bean的屬性......
  2. 容器創建一個ConcurrentHashMap對象,這個mapkeybean的名稱,而value則是BeanDefinition對象的引用。容器將第一步中解析出的每一個BeanDefinitionHolder對象,它對應的bean名稱,以及擁有的BeanDefinition引用放入這個map中。所以,這個map保存了我們在程序中聲明的所有的bean的配置信息。
  3. 容器初始化完成后,開始創建bean,遍歷第二步中的map集合,獲取到bean的名稱以及對應的BeanDefinition對象,通過BeanDefinition對象中的信息,判斷這個bean有沒有定義為延遲加載,若沒有,則需要現在就進行創建。在創建前,先通過BeanDefinition中的配置信息,判斷此bean有沒有depend-on(依賴)其他bean,若有,則先創建當前bean依賴的bean(此處的depend-on不是bean的屬性,而是需要通過配置項進行配置的)。之后,則創建當前遍歷到的bean
  4. bean在創建完成后,通過beanBeanDefinition對象,獲取bean需要注入值的屬性,然后為屬性賦值,若屬性的值類型是其他的bean,則以上面相同的步驟,創建屬性對應的bean
  5. 容器創建一個ConcurrentHashMap,將創建好的單例bean保存在其中。我們在代碼中可以通過容器的getBean方法,傳入bean的名稱或類型獲取單例bean。容器會先去這個map中查找,若map中不存在,且這個bean的配置在第2步中存儲配置信息的map中能夠找到,則創建這個bean,放入存儲beanmap中(因為bean可以配置延遲加載,即第一次獲取時加載);

  Spring中,bean最基本的兩種作用域就是singleton(單例)和prototype(多例),默認為單例。以上過程是單例bean的創建過程,若作用域為prototype,則每一次調用getBean方法,都會創建一個新的bean。順帶一提,創建bean的方式是通過反射機制,這個大部分人應該都知道。


2.4 Bean的生命周期

  上面介紹了一下IoC的實現,而針對每一個單獨的Bean,Spring容器如何處理?這就涉及到Bean的生命周期了。這里就來簡單介紹一下Bean是生命周期,首先通過一張圖了解:

  上面這張圖的描述如下:

  1. 首先由Spring容器創建bean實例;

  2. bean的屬性注入值;

  3. bean實現了各類Aware接口,則調用相應的set方法,比如說,實現了BeanNameAware接口的bean,此時容器將調用beansetBeanName方法,將beanname作為參數;實現了ApplicationContextAware接口的bean,此時容器將執行beansetApplicationContext方法,將bean所在的上下文對象作為參數……

  4. 若容器中包含實現了BeanPostProcessor接口的bean,則此時將調用這些beanpostProcessBeforeInitialization方法,將當前正在創建的bean的引用以及beanname作為參數傳入;

  5. bean實現了InitializingBean接口,此時將調用beanafterPropertiesSet方法;

  6. bean指定了自定義的初始化方法,比如說通過配置文件的init-method選項,則此時將執行這個自定義初始化方法;

  7. 若容器中包含實現了BeanPostProcessor接口的bean,則此時將調用這些beanpostProcessAfterInitialization方法,將當前正在創建的bean的引用以及beanname作為參數傳入;

  8. 這個時候,bean就算是初始化完畢,可以被使用了,在容器銷毀之前,這個對象將一直保存在容器中;

  9. bean實現了DisposableBean接口,則在容器銷毀時,會調用beandestroy方法;

  10. 如果bean定義了自定義的銷毀方法,則在容器銷毀時,會調用這個自定義的銷毀方法;

  11. 容器銷毀成功,bean也被回收;

  以上步驟中,比較重要的一個部分就是BeanPostProcessor接口的部分,關於這個接口,我專門寫了一篇博客,詳情請參考:談談Spring中的BeanPostProcessor接口


三、總結

  以上內容是我根據自己的認識,對SpringIoC做的一次簡單記錄,內容並不全面,因為我目前對它的理解也比較淺顯。在今天閱讀Spring源碼的過程中,我發現它真的比我想象中要復雜很多,或許是我水平有限,又或許是沒有掌握閱讀源碼的方法,讀起來真的非常吃力。總而言之,想要真正讀懂Spring,我還需要很多的學習,希望今后能夠盡快提升自己,早日將Spring吃透。


四、參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM