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

程式設計師DMZ發表於2020-07-15

前言

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

image-20200707094637369

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

究其原因在於,你沒有理解為什麼需要這些步驟,也不知道為什麼要按這個順序執行

筆者在閱讀完整個IOCAOP的原始碼後,希望通過這篇文章講一講我的Spring中Bean生命週期的看法,幫助大家能理解性的記憶整個流程,而不是死記硬背!

基礎知識補充

所謂理解也是建立在有一定知識儲備的基礎上的,所以這裡先補充一些基礎概念

Bean建立的三個階段

Spring在建立一個Bean時是分為三個步驟的

  • 例項化,可以理解為new一個物件
  • 屬性注入,可以理解為呼叫setter方法完成屬性注入
  • 初始化,你可以按照Spring的規則配置一些初始化的方法(例如,@PostConstruct註解)

生命週期的概念

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

後置處理器的分析

按照實現介面進行分類

  1. 直接實現了BeanPostProcessor介面

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

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

這種情況下再為屬性注入階段專門提供兩個方法是不是有點多餘呢?實際上第三種後置處理器是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

至少現在這張圖上缺少了例項化前後後置處理器的執行流程,對吧?

再補充上這一點之後,我們再來看看,屬性注入後緊接著已經是初始化的階段,在初始化階段開始前應該要呼叫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,一個在學習路上匍匐前行的小菜鳥!

相關文章