面試-Spring的生命週期

Spring Bean的生命週期是Spring面試熱點問題。這個問題即考察對Spring的微觀瞭解,又考察對Spring的宏觀認識,想要答好並不容易!本文但願可以從源碼角度入手,幫助面試者完全搞定Spring Bean的生命週期。java

只有四個!

是的,Spring Bean的生命週期只有這四個階段。把這四個階段和每一個階段對應的擴展點糅合在一塊兒雖然沒有問題,可是這樣很是凌亂,難以記憶。要完全搞清楚Spring的生命週期,首先要把這四個階段緊緊記住。實例化和屬性賦值對應構造方法和setter方法的注入,初始化和銷燬是用戶能自定義擴展的兩個階段。在這四步之間穿插的各類擴展點,稍後會講。面試

  • 實例化 Instantiation
  • 屬性賦值 Populate
  • 初始化 Initialization
  • 銷燬 Destruction

實例化 -> 屬性賦值 -> 初始化 -> 銷燬app

主要邏輯都在doCreate()方法中,邏輯很清晰,就是順序調用如下三個方法,這三個方法與三個生命週期階段一一對應,很是重要,在後續擴展接口分析中也會涉及。框架

  • createBeanInstance() -> 實例化
  • populateBean() -> 屬性賦值
  • initializeBean() -> 初始化

源碼以下,能證實實例化,屬性賦值和初始化這三個生命週期的存在。關於本文的Spring源碼都將忽略無關部分,便於理解:源碼分析

請別再問Spring Bean的生命週期了

 

至於銷燬,是在容器關閉時調用的,詳見ConfigurableApplicationContext#close()post

經常使用擴展點spa

Spring生命週期相關的經常使用擴展點很是多,因此問題不是不知道,而是記不住或者記不牢。其實記不住的根本緣由仍是不夠了解,這裏經過源碼+分類的方式幫你們記憶。3d

第一大類:影響多個Bean的接口代理

實現了這些接口的Bean會切入到多個Bean的生命週期中。正由於如此,這些接口的功能很是強大,Spring內部擴展也常用這些接口,例如自動注入以及AOP的實現都和他們有關。xml

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

這兩兄弟多是Spring擴展中最重要的兩個接口!InstantiationAwareBeanPostProcessor做用於實例化階段的先後,BeanPostProcessor做用於初始化階段的先後。正好和第1、第三個生命週期階段對應。經過圖能更好理解:

 

請別再問Spring Bean的生命週期了

 

 

InstantiationAwareBeanPostProcessor實際上繼承了BeanPostProcessor接口,嚴格意義上來看他們不是兩兄弟,而是兩父子。可是從生命週期角度咱們重點關注其特有的對實例化階段的影響,圖中省略了從BeanPostProcessor繼承的方法。

InstantiationAwareBeanPostProcessor extends BeanPostProcessor

InstantiationAwareBeanPostProcessor源碼分析:

postProcessBeforeInstantiation調用點,忽略無關代碼:

請別再問Spring Bean的生命週期了

 

能夠看到,postProcessBeforeInstantiation在doCreateBean以前調用,也就是在bean實例化以前調用的,英文源碼註釋解釋道該方法的返回值會替換本來的Bean做爲代理,這也是Aop等功能實現的關鍵點。

postProcessAfterInstantiation調用點,忽略無關代碼:

請別再問Spring Bean的生命週期了

 

能夠看到該方法在屬性賦值方法內,可是在真正執行賦值操做以前。其返回值爲boolean,返回false時能夠阻斷屬性賦值階段(continueWithPropertyPopulation = false;)。

關於BeanPostProcessor執行階段的源碼穿插在下文Aware接口的調用時機分析中,由於部分Aware功能的就是經過他實現的!只須要先記住BeanPostProcessor在初始化先後調用就能夠了。

第二大類:只調用一次的接口

這一大類接口的特色是功能豐富,經常使用於用戶自定義擴展。

第二大類中又能夠分爲兩類:

  • Aware類型的接口
  • 生命週期接口

無所不知的Aware

Aware類型的接口的做用就是讓咱們可以拿到Spring容器中的一些資源。基本都可以見名知意,Aware以前的名字就是能夠拿到什麼資源,例如BeanNameAware能夠拿到BeanName,以此類推。調用時機須要注意:全部的Aware方法都是在初始化階段以前調用的!

Aware接口衆多,這裏一樣經過分類的方式幫助你們記憶。

Aware接口具體能夠分爲兩組,至於爲何這麼分,詳見下面的源碼分析。以下排列順序一樣也是Aware接口的執行順序,可以見名知意的接口再也不解釋。

Aware Group1

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

Aware Group2

  • EnvironmentAware
  • EmbeddedValueResolverAware 這個知道的人可能很少,實現該接口可以獲取Spring EL解析器,用戶的自定義註解須要支持spel表達式的時候可使用,很是方便。
  • ApplicationContextAware(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware) 這幾個接口可能讓人有點懵,實際上這幾個接口能夠一塊兒記,其返回值實質上都是當前的ApplicationContext對象,由於ApplicationContext是一個複合接口,以下:

 

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
 MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

這裏涉及到另外一道面試題,ApplicationContext和BeanFactory的區別,能夠從ApplicationContext繼承的這幾個接口入手,除去BeanFactory相關的兩個接口就是ApplicationContext獨有的功能,這裏不詳細說明。

Aware調用時機源碼分析

詳情以下,忽略了部分無關代碼。代碼位置就是咱們上文提到的initializeBean方法詳情,這也說明了Aware都是在初始化階段以前調用的!

請別再問Spring Bean的生命週期了

 

能夠看到並非全部的Aware接口都使用一樣的方式調用。Bean××Aware都是在代碼中直接調用的,而ApplicationContext相關的Aware都是經過BeanPostProcessor#postProcessBeforeInitialization()實現的。

感興趣的能夠本身看一下ApplicationContextAwareProcessor這個類的源碼,就是判斷當前建立的Bean是否實現了相關的Aware方法,若是實現了會調用回調方法將資源傳遞給Bean。

至於Spring爲何這麼實現,應該沒什麼特殊的考量。也許和Spring的版本升級有關。基於對修改關閉,對擴展開放的原則,Spring對一些新的Aware採用了擴展的方式添加。

BeanPostProcessor的調用時機也能在這裏體現,包圍住invokeInitMethods方法,也就說明了在初始化階段的先後執行。

關於Aware接口的執行順序,其實只須要記住第一組在第二組執行以前就好了。每組中各個Aware方法的調用順序其實沒有必要記,有須要的時候點進源碼一看便知。

簡單的兩個生命週期接口

至於剩下的兩個生命週期接口就很簡單了,實例化和屬性賦值都是Spring幫助咱們作的,可以本身實現的有初始化和銷燬兩個生命週期階段。

InitializingBean 對應生命週期的初始化階段,在上面源碼的invokeInitMethods(beanName, wrappedBean, mbd);方法中調用。

有一點須要注意,由於Aware方法都是執行在初始化方法以前,因此能夠在初始化方法中放心大膽的使用Aware接口獲取的資源,這也是咱們自定義擴展Spring的經常使用方式。

除了實現InitializingBean接口以外還能經過註解或者xml配置的方式指定初始化方法,至於這幾種定義方式的調用順序其實沒有必要記。由於這幾個方法對應的都是同一個生命週期,只是實現方式不一樣,咱們通常只採用其中一種方式。

DisposableBean 相似於InitializingBean,對應生命週期的銷燬階段,以ConfigurableApplicationContext#close()方法做爲入口,實現是經過循環取全部實現了DisposableBean接口的Bean而後調用其destroy()方法 。感興趣的能夠自行跟一下源碼。

擴展閱讀: BeanPostProcessor 註冊時機與執行順序

註冊時機

咱們知道BeanPostProcessor也會註冊爲Bean,那麼Spring是如何保證BeanPostProcessor在咱們的業務Bean以前初始化完成呢?

請看咱們熟悉的refresh()方法的源碼,省略部分無關代碼:

請別再問Spring Bean的生命週期了

 

能夠看出,Spring是先執行registerBeanPostProcessors()進行BeanPostProcessors的註冊,而後再執行finishBeanFactoryInitialization初始化咱們的單例非懶加載的Bean。

執行順序

BeanPostProcessor有不少個,並且每一個BeanPostProcessor都影響多個Bean,其執行順序相當重要,必須可以控制其執行順序才行。關於執行順序這裏須要引入兩個排序相關的接口:PriorityOrdered、Ordered

PriorityOrdered是一等公民,首先被執行,PriorityOrdered公民之間經過接口返回值排序,Ordered是二等公民,而後執行,Ordered公民之間經過接口返回值排序

都沒有實現是三等公民,最後執行。

在如下源碼中,能夠很清晰的看到Spring註冊各類類型BeanPostProcessor的邏輯,根據實現不一樣排序接口進行分組。優先級高的先加入,優先級低的後加入。

請別再問Spring Bean的生命週期了

 

根據排序接口返回值排序,默認升序排序,返回值越低優先級越高。

/**
 * Useful constant for the highest precedence value.
 * @see java.lang.Integer#MIN_VALUE
 */
 int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
 /**
 * Useful constant for the lowest precedence value.
 * @see java.lang.Integer#MAX_VALUE
 */
 int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

PriorityOrdered、Ordered接口做爲Spring整個框架通用的排序接口,在Spring中應用普遍,也是很是重要的接口。

總結

Spring Bean的生命週期分爲四個階段和多個擴展點。擴展點又能夠分爲影響多個Bean和影響單個Bean。整理以下:

四個階段

  • 實例化 Instantiation
  • 屬性賦值 Populate
  • 初始化 Initialization
  • 銷燬 Destruction

多個擴展點

影響多個Bean

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

影響單個Bean

Aware

  • Aware Group1
  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware
  • Aware Group2
  • EnvironmentAware
  • EmbeddedValueResolverAware
  • ApplicationContextAware(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware)

生命週期

  • InitializingBean
  • DisposableBean
相關文章
相關標籤/搜索