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

前言

當你準備去複習Spring中Bean的生命週期的時候,這個時候你開始上網找資料,很大機率會看到下面這張圖:程序員

image-20200707094637369
image-20200707094637369

先不論這張圖上是否全面,可是就說這張圖吧,你是否是背了又忘,忘了又背?web

究其緣由在於,你沒有理解爲何須要這些步驟,也不知道爲何要按這個順序執行微信

筆者在閱讀完整個IOCAOP的源碼後,但願經過這篇文章講一講個人Spring中Bean生命週期的見解,幫助你們能理解性的記憶整個流程,而不是死記硬背!app

基礎知識補充

所謂理解也是創建在有必定知識儲備的基礎上的,因此這裏先補充一些基礎概念框架

Bean建立的三個階段

Spring在建立一個Bean時是分爲三個步驟的編輯器

  • 實例化,能夠理解爲new一個對象
  • 屬性注入,能夠理解爲調用setter方法完成屬性注入
  • 初始化,你能夠按照Spring的規則配置一些初始化的方法(例如, @PostConstruct註解)

生命週期的概念

Bean的生命週期指的就是在上面三個步驟中後置處理器BeanPostprocessor穿插執行的過程ide

後置處理器的分析

按照實現接口進行分類函數

  1. 直接實現了 BeanPostProcessor接口

最簡單的後置處理器,也就是說直接實現了BeanPostProcessor接口,這種後置處理器只能在初始化先後執行post

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;  }  } 複製代碼
  1. 直接實現了 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;  } } 複製代碼
  1. Spring內部專用的後置處理器

可能有的小夥伴認爲,第三種後置處理器確定就是用來在屬性注入先後執行了的吧。我只能說,大兄弟,太天真了,看看下面這張圖

image-20200707163736980
image-20200707163736980

這種狀況下再爲屬性注入階段專門提供兩個方法是否是有點多餘呢?實際上第三種後置處理器是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;  }  } 複製代碼

通常咱們在探究生命週期的時候都不會考慮這種後置處理器的執行

生命週期詳細介紹

在瞭解了上面的概念後,咱們再來看看這張圖

image-20200707094637369
image-20200707094637369

至少如今這張圖上缺乏了實例化先後後置處理器的執行流程,對吧?

再補充上這一點以後,咱們再來看看,屬性注入後緊接着已是初始化的階段,在初始化階段開始前應該要調用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配置的方式

因此,這三個方法的執行順序爲:

  1. @PostConstruct註解標註的方法
  2. 實現了 InitializingBean接口後複寫的 afterPropertiesSet方法
  3. XML中自定義的初始化方法

在完成初始化,沒什麼好說的了,最後調用一下postProcessAfterInitialization,整個Bean的生命週期到此結束

總結

本文的主要目的是想要幫助你們更好的理解整個Bean的生命週期,不過理解是創建在有必定知識存儲的基礎上的

你至少要對Bean的後置處理器跟Bean建立有一個大概的理解,那麼經過本文你能理清一些細節方面的東西

例如,爲何Aware接口執行在初始化階段以前?爲何初始化的三個方法會按

@PostConstructafterPropertiesSet,XML中定義的初始化方法這個順序執行。

本文也將是我整個Spring關於IOCAOP的最後一篇文字,在這以後我打算作一個Spring事務專題,預計6到7篇文章,事務結束後關於整個Spring源碼的學習也就結束啦!原本預期要一年才能完成,不過由於最近離職了,因此打算全職在家寫完這個系列!

若是本文對你由幫助的話,記得點個贊吧!也歡迎關注個人公衆號,微信搜索:程序員DMZ,或者掃描下方二維碼,跟着我一塊兒認認真真學Java,踏踏實實作一個coder。

公衆號
公衆號

我叫DMZ,一個在學習路上匍匐前行的小菜鳥!

本文使用 mdnice 排版

相關文章
相關標籤/搜索