大廠高頻面試題Spring Bean生命週期最詳解

Tom彈架構發表於2021-12-08

Spring作為當前Java最流行、最強大的輕量級框架。Spring Bean的生命週期也是面試高頻題,瞭解Spring Bean週期也能更好地幫助我們解決日常開發中的問題。程式設計師應該都知道Spring的基礎容器是ApplicationContext。應很多粉絲的強烈建議,本文我來分析分析 ApplicationContext中Bean的生命週期。ApplicationContext是頂層容器介面BeanFactory的實現類,因此,我們瞭解了ApplicationContext的生命週期邏輯,也基本上了解了其他型別容器的生命週期邏輯。

1 Spring生命週期流程圖

下面先來看一張Spring Bean完整的生命週期流程圖,下圖描述的是從Spring容器初始化Bean開始直到Spring容器銷燬Bean,所經歷的關鍵節點。

file

從上圖可以看出,Spring Bean的生命週期管理的基本思路是:在Bean出現之前,先準備操作Bean的BeanFactory,然後操作完Bean,所有的Bean也還會交給BeanFactory進行管理。在所有Bean操作準備BeanPostProcessor作為回撥。在Bean的完整生命週期管理過程中,經歷了以下主要幾個步驟:

1.1 Bean建立前的準備階段

步驟1: Bean容器在配置檔案中找到Spring Bean的定義以及相關的配置,如init-method和destroy-method指定的方法。
步驟2: 例項化回撥相關的後置處理器如BeanFactoryPostProcessor、BeanPostProcessor、InstantiationAwareBeanPostProcessor等

1.2 建立Bean的例項

步驟3: Srping 容器使用Java反射API建立Bean的例項。
步驟4:掃描Bean宣告的屬性並解析。

1.3 開始依賴注入

步驟5:開始依賴注入,解析所有需要賦值的屬性並賦值。
步驟6:如果Bean類實現BeanNameAware介面,則將通過傳遞Bean的名稱來呼叫setBeanName()方法。
步驟7:如果Bean類實現BeanFactoryAware介面,則將通過傳遞BeanFactory物件的例項來呼叫setBeanFactory()方法。
步驟8:如果有任何與BeanFactory關聯的BeanPostProcessors物件已載入Bean,則將在設定Bean屬性之前呼叫postProcessBeforeInitialization()方法。
步驟9:如果Bean類實現了InitializingBean介面,則在設定了配置檔案中定義的所有Bean屬性後,將呼叫afterPropertiesSet()方法。

1.4 快取到Spring容器

步驟10: 如果配置檔案中的Bean定義包含init-method屬性,則該屬性的值將解析為Bean類中的方法名稱,並將呼叫該方法。
步驟11:如果為Bean Factory物件附加了任何Bean 後置處理器,則將呼叫postProcessAfterInitialization()方法。

1.5 銷燬Bean的例項

步驟12:如果Bean類實現DisposableBean介面,則當Application不再需要Bean引用時,將呼叫destroy()方法。
步驟13:如果配置檔案中的Bean定義包含destroy-method屬性,那麼將呼叫Bean類中的相應方法定義。

2 程式碼實戰演示

下面我們用一個簡單的Bean來演示並觀察一下Spring Bean完整的生命週期。

2.1 準備Author類

1、首先是一個簡單的Bean,呼叫Bean自身的方法和Bean級生命週期介面方法,為了方便演示,它實現了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這4個介面,同時新增2個init-method和destory-method方法,對應配置檔案中的init-method和destroy-method。具體程式碼如下:


package com.tom.lifecycle;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

@Slf4j
@Data
public class Author implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
    private String name;
    private String address;
    private int age;

    private BeanFactory beanFactory;

    private String beanName;

    public Author() {
        log.info("【構造器】呼叫Tom類的構造器例項化");
    }

    public void setName(String name) {
        log.info("【注入屬性】name");
        this.name = name;
    }

    public void setAddress(String address) {
        log.info("【注入屬性】address");
        this.address = address;
    }

    public void setAge(int age) {
        log.info("【注入屬性】age");
        this.age = age;
    }

    // 實現BeanFactoryAware介面的方法
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("【BeanFactoryAware介面】呼叫setBeanFactory方法");
        this.beanFactory = beanFactory;
    }

    // 實現BeanNameAware介面的方法
    public void setBeanName(String beanName) {
        log.info("【BeanNameAware介面】呼叫setBeanName方法");
        this.beanName = beanName;
    }

    // 實現DiposibleBean介面的方法
    public void destroy() throws Exception {
        log.info("【DiposibleBean介面】呼叫destroy方法");
    }

    // 實現InitializingBean介面的方法
    public void afterPropertiesSet() throws Exception {
        log.info("【InitializingBean介面】呼叫afterPropertiesSet方法");
    }

    // 通過<bean>的init-method屬性指定的初始化方法
    public void beanInit() {
        log.info("【init-method】呼叫<bean>的init-method屬性指定的初始化方法");
    }

    // 通過<bean>的destroy-method屬性指定的初始化方法
    public void beanDestory() {
        log.info("【destroy-method】呼叫<bean>的destroy-method屬性指定的初始化方法");
    }
}

在配置Spring配置檔案中加入如下內容:


	<bean id="author" class="com.tom.lifecycle.Author"
		  init-method="beanInit"
		  destroy-method="beanDestory"
		  scope="singleton"
		  p:name="Tom" p:address="湖南長沙" p:age="18"/>
			

2.2 演示BeanFactoryPostProcessor的執行

1.建立GPBeanFactoryPostProcessor類,並實現BeanFactoryPostProcessor介面,具體程式碼如下:


package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

@Slf4j
public class GPBeanFactoryPostProcessor implements BeanFactoryPostProcessor{

    public GPBeanFactoryPostProcessor() {
        super();
        log.info("呼叫BeanFactoryPostProcessor實現類構造器!!");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        log.info("BeanFactoryPostProcessor呼叫postProcessBeanFactory方法");
        BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("author");
        bd.getPropertyValues().addPropertyValue("age", "16");
    }
}

2.在配置Spring配置檔案中加入如下內容:


	<bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />

3.編寫測試類BeanLifeCycleTest,具體程式碼如下:


package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class BeanLifeCycleTest {

    public static void main(String[] args) {

        log.info("====== 開始初始化Spring容器 ========");

        ApplicationContext factory = new ClassPathXmlApplicationContext("application-beans.xml");

        log.info("====== 初始化Spring容器成功 ========");

        //獲取Author例項
        Author author = factory.getBean("author", Author.class);

        log.info(author.toString());

        log.info("====== 開始銷燬Spring容器 ========");

        ((ClassPathXmlApplicationContext) factory).registerShutdownHook();
    }

}

4.執行結果

執行結果如下:


15:49:12.477 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - 呼叫BeanPostProcessor實現類構造器!!
15:49:12.494 [main] INFO com.tom.lifecycle.Author - 【構造器】呼叫Tom類的構造器例項化
15:49:12.527 [main] INFO com.tom.lifecycle.Author - 【注入屬性】address
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【注入屬性】age
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【注入屬性】name
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【BeanNameAware介面】呼叫setBeanName方法
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【BeanFactoryAware介面】呼叫setBeanFactory方法
15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor介面方法postProcessBeforeInitialization對屬性進行更改
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【InitializingBean介面】呼叫afterPropertiesSet方法
15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【init-method】呼叫<bean>的init-method屬性指定的初始化方法
15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor介面方法postProcessAfterInitialization對屬性進行更改
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 初始化Spring容器成功 ========
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=湖南長沙, age=18, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,author]; root of factory hierarchy, beanName=author)
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 開始銷燬Spring容器 ========
15:49:12.532 [Thread-0] INFO com.tom.lifecycle.Author - 【DiposibleBean介面】呼叫destroy方法
15:49:12.533 [Thread-0] INFO com.tom.lifecycle.Author - 【destroy-method】呼叫<bean>的destroy-method屬性指定的初始化方法

我們看到,整個執行和我們一開始繪製的流程圖一致。但是為什麼我們要實現BeanFactoryPostProcessor介面呢?我們進入到BeanFactoryPostProcessor的原始碼如下:


package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

BeanFactoryPostProcessor介面只有一個postProcessBeanFactory()方法,BeanFactoryPostProcessor:在BeanFactory標準初始化之後可以進行修改。將載入所有Bean定義,但是還沒有例項化Bean。這個方法允許重新覆蓋或者新增屬性甚至快速的初始化bean。初次看到可能不知道postProcessBeanFactory()到底是幹嘛的。要想透徹理解這個方法的作用,下面來進入到BeanFactoryPostProcessor的原始碼,理解一下postProcessBeanFactory()的引數,我們可以利用這些引數做一些操作。

通過引數來看,只有一個ConfigurableListableBeanFactory類,這個類的可以提供分析、修改Bean定義和預先例項化單例的功能。我們再進入到ConfigurableListableBeanFactory的原始碼中:


public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {

		//忽略被給定注入依賴型別 ,例如String
    void ignoreDependencyType(Class<?> var1);

		//略被給定注入依賴介面 。這個通常被使用由ApplicationContext去註冊依賴,可以以多種方式實現。例如BeanFactory通過BeanFactoryAware,ApplicationContext 通過ApplicationContextAware。預設情況下,僅BeanFactoryAware介面是被忽略,需要忽略其他介面,呼叫此方法
    void ignoreDependencyInterface(Class<?> var1);

		//註冊一個特定型別依賴伴隨著相應的Autowired值。這個是準備被用於應該可以Autowire而不是在這個工廠被定義的Bean的工廠/上下文引用。例如 將ApplicationContext型別的依賴項解析為Bean所在的ApplicationContext例項。注意~在普通的BeanFactory中沒有註冊這樣的預設型別,甚至連BeanFactory介面本身都沒有
    void registerResolvableDependency(Class<?> var1, Object var2);

		//確認這個被指定的Bean是否是一個Autowire候選,將被注入到其他宣告匹配型別的依賴的Bean中
    boolean isAutowireCandidate(String var1, DependencyDescriptor var2) throws NoSuchBeanDefinitionException;

		//根據指定的beanName返回被註冊的Bean定義,允許訪問其屬性值和建構函式引數值(可以在BeanFactory後期處理期間被修改)。這個被返回的BeanDefinition物件不應該是副本而是原始在工廠被註冊的。這意味著如果需要它可以被轉換為更具體的實現型別。注意這個方法只能獲得本地工廠BeanDefinition
    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;

		//凍結全部Bean定義,給被註冊的Bean定義發訊號告訴它們今後不再被修改和進一步後續處理。它允許Factory去積極快取Bean定義後設資料
    void freezeConfiguration();

		//返回該工廠的BeanDefinnition是否被凍結
    boolean isConfigurationFrozen();

		//確保所有非懶載入的單例Bean被例項化,包括FactoryBean
    void preInstantiateSingletons() throws BeansException;
}

通過以上演示和分析,我們應該大概能夠了解ConfigurableListableBeanFactory的作用,基本就都是對於Bean定義的操作。至此我們還沒有看到BeanPostProcessor 和InstantiationAwareBeanPostProcessor的呼叫。下面我們把BeanPostProcessor 和InstantiationAwareBeanPostProcessor的實現補充上來,再看完整的執行流程

2.3 實現BeanPostProcessor

建立GPBeanPostProcessor類,並實現BeanPostProcessor 介面,具體程式碼如下:


package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class GPBeanPostProcessor implements BeanPostProcessor {

    public GPBeanPostProcessor(){
        log.info("呼叫BeanPostProcessor實現類構造器!!");
    }
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        log.info("BeanPostProcessor介面方法postProcessBeforeInitialization對屬性進行更改");
        return o;
    }

    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        log.info("BeanPostProcessor介面方法postProcessAfterInitialization對屬性進行更改");
        return o;
    }
}

ApplicationContext 可以在BeanDefinition中自動檢測到實現了BeanPostProcessor的Bean,並且把這些Bean應用於隨後的Bean建立。普通的BeanFactory允許對後處理器進行程式化註冊,通過工廠應用於所有Bean建立。BeanPostProcessor介面中主要有兩個方法:

方法名 解釋
postProcessBeforeInitialization 在Bean例項化回撥(例如InitializingBean的afterPropertiesSet 或者一個定製的init-method)之前應用此BeanPostProcessor
postProcessAfterInitialization 在bean例項化回撥(例如InitializingBean的afterPropertiesSet 或者一個定製的init-method)之後應用此BeanPostProcessor

2.4 實現InstantiationAwareBeanPostProcessor

建立GPInstantiationAwareBeanPostProcessor類,並實現InstantiationAwareBeanPostProcessorAdapter介面,具體程式碼如下:


package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;

import java.beans.PropertyDescriptor;

@Slf4j
public class GPInstantiationAwareBeanPostProcessor  extends InstantiationAwareBeanPostProcessorAdapter {

    public GPInstantiationAwareBeanPostProcessor() {
        super();
        log.info("呼叫InstantiationAwareBeanPostProcessorAdapter實現類構造器!!");
    }

    // 介面方法、例項化Bean之前呼叫
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass,String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor呼叫postProcessBeforeInstantiation方法");
        return null;
    }

    // 介面方法、例項化Bean之後呼叫
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor呼叫postProcessAfterInitialization方法");
        return bean;
    }

    // 介面方法、設定某個屬性時呼叫
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor呼叫postProcessPropertyValues方法");
        return pvs;
    }
}

實現InstantiationAwareBeanPostProcessorAdapter的Bean之後,可以在例項化成功之後,做一些校驗或者補充些內容或者把Bean包裝代理注入。實現InstantiationAwareBeanPostProcessorAdapter的Bean之後,不會影響容器正常處理每一個例項化的Bean,其子類僅僅只是根據需要覆蓋父類的方法。

注意,只有在實際需要 InstantiationAwareBeanPostProcessor 功能時才推薦此基類。如果我們所需要的只是簡單的BeanPostProcessor功能,那麼直接實現更簡單的介面即可。

下面詳細介紹一下InstantiationAwareBeanPostProcessorAdapter介面中的所有方法:

方法名 解釋
postProcessBeforeInstantiation 在例項化目標Bean之前應用此BeanPostProcessor。這個返回的Bean也許是一個代理代替目標Bean,有效地抑制目標Bean的預設例項化。如果此方法返回一個非空物件,則Bean的建立過程將被短路。唯一的進一步處理被應用是BeanPostProcessor.postProcessAfterInitialization(java.lang.Object, java.lang.String)方法(改變了Bean的生命週期例項化之後直接進入BeanPostProcessor.postProcessAfterInitialization)回撥來自於配置好的BeanPostProcessors。這個回撥將僅被應用於有Bean Class的BeanDefintions。特別是,它不會應用於採用”factory-method“的Bean。後處理器可以實現擴充套件的SmartInstantiationAwareBeanPostProcessor介面,以便預測它們將返回的Bean物件的型別
postProcessPropertyValues 在工廠將給定的屬性值應用到給定的Bean之前,對給定的屬性值進行後處理。允許檢查全部依賴是否已經全部滿足,例如基於一個@Required在Bean屬性的Setter方法上。還允許替換要應用的屬性值,通常通過基於原始的PropertyValues建立一個新的MutablePropertyValues例項,新增或刪除特定的值
postProcessAfterInitialization 在Bean初始化回撥(如InitializingBean的afterPropertiesSet或者定製的init-method)之後,應用這個BeanPostProcessor去給一個新的Bean例項。Bean已經配置了屬性值,返回的Bean例項可能已經被包裝。
如果是FactoryBean,這個回撥將為FactoryBean例項和其他被FactoryBean建立的物件所呼叫。這個post-processor可以通過相應的FactoryBean例項去檢查決定是否應用FactoryBean或者被建立的物件或者兩個都有。這個回撥在一個由InstantiationAwareBeanPostProcessor短路的觸發之後將被呼叫

2.5 修改配置檔案

完整的配置檔案內容如下:


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

		<bean id="beanPostProcessor" class="com.tom.lifecycle.GPBeanPostProcessor" />

		<bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />

		<bean id="instantiationAwareBeanPostProcessor" class="com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor" />


		<bean id="author" class="com.tom.lifecycle.Author"
				init-method="beanInit"
				destroy-method="beanDestory"
				scope="singleton"
				p:name="Tom" p:address="湖南長沙" p:age="18"/>

</beans>

2.6 執行結果

最後,我們再次執行BeanLifeCycleTest測試類,看到如下執行結果:

15:56:20.030 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - 呼叫BeanFactoryPostProcessor實現類構造器!!
15:56:20.045 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - BeanFactoryPostProcessor呼叫postProcessBeanFactory方法
15:56:20.046 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - 呼叫BeanPostProcessor實現類構造器!!
15:56:20.047 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - 呼叫InstantiationAwareBeanPostProcessorAdapter實現類構造器!!
15:56:20.051 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor呼叫postProcessBeforeInstantiation方法
15:56:20.052 [main] INFO com.tom.lifecycle.Author - 【構造器】呼叫Tom類的構造器例項化
15:56:20.069 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor呼叫postProcessPropertyValues方法
15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入屬性】address
15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入屬性】age
15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入屬性】name
15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【BeanNameAware介面】呼叫setBeanName方法
15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【BeanFactoryAware介面】呼叫setBeanFactory方法
15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor介面方法postProcessBeforeInitialization對屬性進行更改
15:56:20.093 [main] INFO com.tom.lifecycle.Author - 【InitializingBean介面】呼叫afterPropertiesSet方法
15:56:20.093 [main] INFO com.tom.lifecycle.Author - 【init-method】呼叫<bean>的init-method屬性指定的初始化方法
15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor介面方法postProcessAfterInitialization對屬性進行更改
15:56:20.093 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor呼叫postProcessAfterInitialization方法
15:56:20.097 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 初始化Spring容器成功 ========
15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=湖南長沙, age=16, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,beanFactoryPostProcessor,instantiationAwareBeanPostProcessor,author]; root of factory hierarchy, beanName=author)
15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 開始銷燬Spring容器 ========
15:56:20.099 [Thread-0] INFO com.tom.lifecycle.Author - 【DiposibleBean介面】呼叫destroy方法
15:56:20.100 [Thread-0] INFO com.tom.lifecycle.Author - 【destroy-method】呼叫<bean>的destroy-method屬性指定的初始化方法

3 Spring Bean生命週期執行時序圖

最後我們來看一下完整的執行時序圖:

file

關注微信公眾號『 Tom彈架構 』回覆“Spring”可獲取完整原始碼。

本文為“Tom彈架構”原創,轉載請註明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支援是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術乾貨!

相關文章