【Spring】面試官,請別再問Spring Bean的生命週期了!

目錄

一、Bean的生命週期

二、常用擴展點

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

2.1.1 InstantiationAwareBeanPostProcessor

2.1.2 BeanPostProcesso

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

2.2.1 無所不知的Aware

2.2.2 簡單的兩個生命週期接口

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

3.1 註冊時機

3.2 執行順序

四、總結

4.1 四個階段

4.2 多個擴展點

4.3 Bean的生命週期流程圖


之前看到過一篇對Bean生命週期講解的很不錯的博客,現在我對其再做一些整理補充分享給大家,以供學習使用。

一、Bean的生命週期

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

只有四個!

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

  1. 實例化 Instantiation
  2. 屬性賦值 Populate
  3. 初始化 Initialization
  4. 銷燬 Destruction

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

 

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

  1. createBeanInstance() -> 實例化
  2. populateBean() -> 屬性賦值
  3. initializeBean() -> 初始化

注:bean的生命週期是從將bean定義全部註冊到BeanFacotry中以後開始的。

 

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

創建Bean:

// 忽略了無關代碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (instanceWrapper == null) {
       // 實例化階段!
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       // 屬性賦值階段!
      populateBean(beanName, mbd, instanceWrapper);
       // 初始化階段!
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
}

 上面這些這個實例化Bean的方法是在getBean()方法中調用的,而getBean是在finishBeanFactoryInitialization方法中調用的,用來實例化單例非懶加載Bean,源碼如下:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.

            // 所有BeanPostProcesser初始化的調用點
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.

            // 所有單例非懶加載Bean的調用點
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            finishRefresh();
        }
}

 銷燬Bean:

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

 

二、常用擴展點

Spring生命週期相關的常用擴展點非常多,所以問題不是不知道,而是記不住或者記不牢。其實記不住的根本原因還是不夠了解,這裏通過源碼+分類的方式幫大家記憶。

 

區分影響一個bean或者多個bean是從源碼分析得出的.

以BeanPostProcessor爲例:

  1. 從refresh方法來看,BeanPostProcessor 實例化比正常的bean早.
  2. 從initializeBean方法看,每個bean初始化前後都調用所有BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法.

 

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

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

  • InstantiationAwareBeanPostProcessor
  • BeanPostProcessor

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

2.1.1 InstantiationAwareBeanPostProcessor

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

InstantiationAwareBeanPostProcessor extends BeanPostProcessor

 

InstantiationAwareBeanPostProcessor源碼分析:

  • postProcessBeforeInstantiation調用點,忽略無關代碼:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    try {
        // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        // postProcessBeforeInstantiation方法調用點,這裏就不跟進了,
        // 有興趣的同學可以自己看下,就是for循環調用所有的InstantiationAwareBeanPostProcessor
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    
    try {   
        // 上文提到的doCreateBean方法,可以看到
        // postProcessBeforeInstantiation方法在創建Bean之前調用
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    
}

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

 

  • postProcessAfterInstantiation調用點,忽略無關代碼:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
    // state of the bean before properties are set. This can be used, for example,
    // to support styles of field injection.
    boolean continueWithPropertyPopulation = true;

     // InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
     // 方法作爲屬性賦值的前置檢查條件,在屬性賦值之前執行,能夠影響是否進行屬性賦值!
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
       for (BeanPostProcessor bp : getBeanPostProcessors()) {
          if (bp instanceof InstantiationAwareBeanPostProcessor) {
             InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
             if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                continueWithPropertyPopulation = false;
                break;
             }
          }
       }
    }
 
    // 忽略後續的屬性賦值操作代碼
}

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

 

2.1.2 BeanPostProcesso

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

 

接口源碼:

public interface BeanPostProcessor {
     //bean初始化之前調用
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	
    //bean初始化之後調用
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

 

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

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

第二大類中又可以分爲兩類:

  1. Aware類型的接口
  2. 生命週期接口

 

2.2.1 無所不知的Aware

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

Aware接口衆多,這裏同樣通過分類的方式幫助大家記憶。

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

 

Aware Group1

  1. BeanNameAware
  2. BeanClassLoaderAware
  3. BeanFactoryAware

Aware Group2

  1. EnvironmentAware
  2. EmbeddedValueResolverAware 這個知道的人可能不多,實現該接口能夠獲取Spring EL解析器,用戶的自定義註解需要支持spel表達式的時候可以使用,非常方便。
  3. ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 這幾個接口可能讓人有點懵,實際上這幾個接口可以一起記,其返回值實質上都是當前的ApplicationContext對象,因爲ApplicationContext是一個複合接口,如下:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

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

 

Aware調用時機源碼分析

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

// 見名知意,初始化階段調用的方法
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 這裏調用的是Group1中的三個Bean開頭的Aware
    invokeAwareMethods(beanName, bean);

    Object wrappedBean = bean;
    
    // 這裏調用的是Group2中的幾個Aware,
    // 而實質上這裏就是前面所說的BeanPostProcessor的調用點!
    // 也就是說與Group1中的Aware不同,這裏是通過BeanPostProcessor(ApplicationContextAwareProcessor)實現的。
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

    // 這個是初始化方法,下文要介紹的InitializingBean調用點就是在這個方法裏面
    invokeInitMethods(beanName, wrappedBean, mbd);

    // BeanPostProcessor的另一個調用點
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    return wrappedBean;
}

可以看到並不是所有的Aware接口都使用同樣的方式調用。Bean××Aware都是在代碼中直接調用的,而ApplicationContext相關的Aware都是通過BeanPostProcessor#postProcessBeforeInitialization()實現的。感興趣的可以自己看一下ApplicationContextAwareProcessor這個類的源碼,就是判斷當前創建的Bean是否實現了相關的Aware方法,如果實現了會調用回調方法將資源傳遞給Bean。

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

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

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

 

2.2.2 簡單的兩個生命週期接口

至於剩下的兩個生命週期接口就很簡單了,實例化和屬性賦值都是Spring幫助我們做的,能夠自己實現的有初始化和銷燬兩個生命週期階段。

 

2.2.2.1 InitializingBean接口

InitializingBean顧名思義,是初始化Bean相關的接口。

接口定義:

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

}

看方法名,是在讀完Properties文件,之後執行的方法。afterPropertiesSet()方法是在初始化過程中被調用的。

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

有一點需要注意,因爲Aware方法都是執行在初始化方法之前,所以可以在初始化方法中放心大膽的使用Aware接口獲取的資源,這也是我們自定義擴展Spring的常用方式。

除了實現InitializingBean接口之外還能通過註解(@PostConstruct)或者xml配置的方式指定初始化方法(init-method),至於這幾種定義方式的調用順序其實沒有必要記。因爲這幾個方法對應的都是同一個生命週期,只是實現方式不同,我們一般只採用其中一種方式。

 

三種實現指定初始化方法的方法:

  • 使用@PostConstruct註解,該註解作用於void方法上
  • 在配置文件中配置init-method方法
<bean id="student" class="com.demo.Student" init-method="init2">
        <property name="name" value="小明"></property>
        <property name="age" value="20"></property>
        <property name="school" ref="school"></property>
</bean>
  • 將類實現InitializingBean接口
@Component("student")
public class Student implements InitializingBean{
    private String name;
    private int age;
	        …
}

執行:

@Component("student")
public class Student implements InitializingBean{
    private String name;
    private int age;
    
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
       
    //1.使用postconstrtct註解
    @PostConstruct
    public void init(){
        System.out.println("執行 init方法");
    }
     
    //2.在xml配置文件中配置init-method方法
    public void init2(){
        System.out.println("執行init2方法 ");
    }
    
    //3.實現InitializingBean接口
    public void afterPropertiesSet() throws Exception {
        System.out.println("執行init3方法");
    }
    
}

通過測試我們可以得出結論,三種實現方式的執行順序是:

Constructor > @PostConstruct > InitializingBean > init-method

 

2.2.2.2 DisposableBean接口

DisposableBean 類似於InitializingBean,對應生命週期的銷燬階段,以ConfigurableApplicationContext#close()方法作爲入口,實現是通過循環獲取所有實現了DisposableBean接口的Bean然後調用其destroy()方法 。

 

接口定義:

public interface DisposableBean {
    void destroy() throws Exception;
}

 

定義一個實現了DisposableBean接口的Bean:

public class IndexBean implements InitializingBean,DisposableBean {
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("init-afterPropertiesSet()");
    }
    public void test(){
        System.out.println("init-test()");
    }
}

 

執行:

public class Main {
    public static void main(String[] args) {
        AbstractApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:application-usertag.xml");
        System.out.println("init-success");
        applicationContext.registerShutdownHook();
    }
}

 

執行結果:

init-afterPropertiesSet()
init-test()
init-success
destroy

也就是說,在對象銷燬的時候,會去調用DisposableBean的destroy方法。在進入到銷燬過程時先去調用一下DisposableBean的destroy方法,然後後執行 destroy-method聲明的方法(用來銷燬Bean中的各項數據)。

 

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

首先要明確一個概念,在spring中一切皆bean

所有的組件都會被作爲一個bean裝配到spring容器中,過程如下圖:

所以我們前面所講的那些拓展點,也都會被作爲一個個bean裝配到spring容器中

 

3.1 註冊時機

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

請看我們熟悉的refresh()方法的源碼,省略部分無關代碼(refresh的詳細註解見refresh()):

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            // 註冊所有BeanPostProcesser的方法
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            // 所有單例非懶加載Bean的創建方法
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }
}

可以看出,Spring是先執行registerBeanPostProcessors()進行BeanPostProcessors的註冊,然後再執行finishBeanFactoryInitialization創建我們的單例非懶加載的Bean。

 

3.2 執行順序

BeanPostProcessor有很多個,而且每個BeanPostProcessor都影響多個Bean,其執行順序至關重要,必須能夠控制其執行順序才行。關於執行順序這裏需要引入兩個排序相關的接口:PriorityOrdered、Ordered

  • PriorityOrdered是一等公民,首先被執行,PriorityOrdered公民之間通過接口返回值排序
  • Ordered是二等公民,然後執行,Ordered公民之間通過接口返回值排序
  • 都沒有實現是三等公民,最後執行

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

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入實現了PriorityOrdered接口的BeanPostProcessors,順便根據PriorityOrdered排了序
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}

sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 然後,加入實現了Ordered接口的BeanPostProcessors,順便根據Ordered排了序
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.

// 最後加入其他常規的BeanPostProcessors
boolean reiterate = true;
while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
        if (!processedBeans.contains(ppName)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
            reiterate = true;
        }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
}

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

/**
 * 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。整理如下:

4.1 四個階段

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

 

4.2 多個擴展點

  • 影響多個Bean
    • BeanPostProcessor
    • InstantiationAwareBeanPostProcessor
  • 影響單個Bean
    • Aware
      • Aware Group1
        • BeanNameAware
        • BeanClassLoaderAware
        • BeanFactoryAware
      • Aware Group2
        • EnvironmentAware
        • EmbeddedValueResolverAware
        • ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)
    • 生命週期
      • InitializingBean
      • DisposableBean

 

4.3 Bean的生命週期流程圖


原文鏈接:https://www.jianshu.com/p/1dec08d290c1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章