Spring Bean 的生命週期

C_nullptr發表於2021-03-03

除了使用階段外,Spring 將 bean 的生命週期定義為例項化、屬性填充、初始化和銷燬四個階段,併為每個階段提供了多個擴充點用於自定義 bean 的建立過程。這篇文章介紹了 bean 的生命週期和其各個擴充點,通過圖示生動的展示、並結合一個例項來演示全過程。

Spring 生命週期

graph LR ClassLoad(類載入) --> Instantiation[例項化] Instantiation --> Populate[屬性填充] Populate --> Initialization[初始化] Initialization --> Using(使用) Using --> Destruction[銷燬]

上圖中,每個箭頭上都有 Spring 提供的擴充點。類載入不是 bean 生命週期的一部分,保留在圖上純粹是因為要提供第一個右向的箭頭,而使用階段也不在討論範圍內,故這兩個採用圓角矩形。

擴充點的型別

Spring 提供的擴充點可以分為:

  • 用於單個 bean 感知自身過程的專用擴充點:下方出現的、以 Aware 結尾為名的介面,org.springframework.beans.factory.InitializingBean 介面和 org.springframework.beans.factory.DisposableBean 介面,這些介面都只定義了一個函式。bean 通過實現這些介面、重寫其中的函式來實現擴充點。Spring 容器會在適當的時候呼叫這些函式。

  • 用於所有普通 bean 初始化的通用擴充點:位於 org.springframework.beans.factory.config 包的 BeanPostProcessorInstantiationAwareBeanPostProcessorDestructionAwareBeanPostProcessor 介面,這些介面中定義了多個擴充點,使用時需要定義一個專門的類實現介面、重寫必要的函式。Spring 容器會將這些實現類優先註冊為 bean,待它們初始化完成後再初始化普通的 bean,在每個普通 bean 註冊時,Spring 容器都會嘗試呼叫所有已註冊的通用擴充點。

    classDiagram BeanPostProcessor <|-- InstantiationAwareBeanPostProcessor BeanPostProcessor <|-- DestructionAwareBeanPostProcessor BeanPostProcessor: +postProcessBeforeInitialization() BeanPostProcessor: +postProcessAfterInitialization() InstantiationAwareBeanPostProcessor: +postProcessBeforeInstantiation() InstantiationAwareBeanPostProcessor: +postProcessAfterInstantiation() DestructionAwareBeanPostProcessor: +postProcessBeforeDestruction()

1. 例項化

建立 bean 物件例項的過程,包括使用工廠模式建立和呼叫建構函式。Spring 通過 InstantiationAwareBeanPostProcessor 介面在例項化前和後各提供了兩個通用擴充點,加上物件例項化的過程,執行順序如下:

  1. postProcessBeforeInstantiation:在普通 bean 物件例項化開始之前呼叫
  2. 物件例項化
  3. postProcessAfterInstantiation:在普通 bean 物件例項化完成之後呼叫

2. 屬性填充

如果物件中有 setter 函式,並通過配置後設資料指定了注入的屬性,Spring 容器會在這一步為其注入配置的值。完成屬性填充後,Spring 通過 Aware(意為感知) 介面提供了十個專用擴充點,這些擴充點用於在 bean 自身內部、感知一些外部資訊使用。呼叫順序如下(下面提到的多個 Aware 介面中,前三個在 org.springframework.beans.factory 包中,4 ~ 9 在 org.springframework.context 包中,最後一個在 org.springframework.web.context 包中 ):

  1. BeanNameAware#setBeanName
  2. BeanClassLoaderAware#setBeanClassLoader
  3. BeanFactoryAware#setBeanFactory
  4. EnvironmentAware#setEnvironment
  5. EmbeddedValueResolverAware#setEmbeddedValueResolver
  6. ResourceLoaderAware#setResourceLoader(僅在 ApplicationContext 中有效)
  7. ApplicationEventPublisherAware#setApplicationEventPublisher(僅在 ApplicationContext 中有效)
  8. MessageSourceAware#setMessageSource(僅在 ApplicationContext 中有效)
  9. ApplicationContextAware#setApplicationContext(僅在 ApplicationContext 中有效)
  10. 如果是 Web 應用程式,還有 ServletContextAware#setServletContext(僅在 WebApplicationContext 中有效)

Aware 擴充點結束之後,還有一個用於在初始化之前、進行屬性校驗的 InitializingBean#afterPropertiesSet 專用擴充點。

3. 初始化

初始化是指通過 bean 在將要工作前進行的最後準備工作,通常是 @BeaninitMethod 屬性定義的函式執行的過程 。Spring 通過 BeanPostProcessor 介面在初始化之前和之後提供了兩個通用擴充點,加上初始化函式執行順序為:

  1. postProcessBeforeInitialization
  2. 自定義的初始化函式
  3. postProcessAfterInitialization

4. 銷燬

銷燬是指 bean 釋放其佔用的一些外部資源的過程,通常是 @Bean 註解的 destroyMethod 屬性定義的銷燬函式執行的過程。Spring 通過 DestructionAwareBeanPostProcessor#postProcessBeforeDestruction 的通用擴充點,再加上 DisposableBean#destroy 提供了的專用擴充點,三者執行順序為:

  1. DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
  2. DisposableBean#destroy
  3. 自定義的銷燬函式。

例項演示

一個簡單的演示,實現了上文提到的 bean 生命週期全部的擴充點,通過日誌列印的方式觀察執行順序來直觀的感受。

在一個 bean 中實現全部的專用擴充點

SimpleBean 類

/**
 * 一個簡單的 bean 例項,實現了 Spring 提供的 bean 生命週期全擴充點,內部函式實現順序即為 bean 生命週期中各個函式的呼叫順序
 */
@Slf4j(topic = "簡單Bean")
public class SimpleBean
        implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,
        EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware,
        ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private EmptyBean emptyBean;

    static {
        //類載入期間 logger 還沒有初始化,使用標準輸出
        System.out.printf("[%s] 簡單Bean : SimpleBean 類載入 loaded%n", Thread.currentThread().getName());
    }

    public SimpleBean() {
        log.info("建構函式執行,建立例項");
    }

    @Autowired
    public void setEmptyBean(EmptyBean emptyBean) {
        this.emptyBean = emptyBean;
        log.info("setter 函式執行,裝配了 {}", this.emptyBean);
    }

    /** 用於通知 bean 感知自身名稱 */
    @Override
    public void setBeanName(String name) {
        log.info("bean 名稱為 {}", name);
    }

    /** 用於通知 bean 感知載入自身的類載入器 */
    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        log.info("類載入器是 {}", classLoader);
    }

    /** 用於通知 bean 感知建立自身的 bean 工廠 */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        //BeanFactory 可能會重寫 toString(),造成日誌輸出過多不便於查閱
        log.info("由 {}@{} 建立", beanFactory.getClass(), beanFactory.hashCode());
    }

    /** 設定該 bean 的執行環境資訊 */
    @Override
    public void setEnvironment(Environment environment) {
        //environment.toString() 會將所有環境資訊輸出,造成日誌輸出過多不便於查閱
        log.info("執行的 JVM 型號是 {}", environment.getProperty("java.vm.name"));
    }

    /** 設定嵌入式配置解析器,可用於解析嵌入在應用程式包內的配置檔案中的文字值 */
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        //在 application.properties 中定義了 editor.name 資訊
        log.info("作者是 {}", resolver.resolveStringValue("${editor.name}"));
    }

    /** 設定用於資源解析的解析器,可用於解析任意格式的資源 */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        log.info("資源解析器物件:{}", resourceLoader);
    }

    /** 設定事件釋出器,與 Spring 提供的事件釋出訂閱機制有關 */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        log.info("事件釋出器物件:{}", applicationEventPublisher);
    }

    /** 設定訊息原,可用於實現國際化 */
    @Override
    public void setMessageSource(MessageSource messageSource) {
        log.info("訊息源物件:{}", messageSource);
    }

    /** 為當前 bean 傳入 ApplicationContext 引用,可使用該容器引用獲取其他 bean 的引用 */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("應用程式上下文物件:{}", applicationContext.getDisplayName());
    }

    /** 在所有屬性設定和 Aware 介面定義的行為都執行完成後,由 BeanFactory 呼叫,bean 可在此校驗自身配置並最終初始化 */
    @Override
    public void afterPropertiesSet() {
        log.info("屬性裝配全部完成,校驗無誤,開始初始化");
    }

    /** 自定義的初始化方法 */
    public void initMethod() {
        log.info("自定義的初始化方法");
    }

    /** 容器銷燬時呼叫 */
    @Override
    public void destroy() {
        log.info("容器即將關閉,銷燬其中的 bean");
    }

    /** 自定義的銷燬方法 */
    public void destroyMethod() {
        log.info("自定義的銷燬方法");
    }

}
/** 一個空的 bean,用於在 SimpleBean 中進行 setter 注入 */
@Component
public class EmptyBean {
}

自定義三種型別的通用擴充點

例項化處理器

@Slf4j(topic = "自定義例項化處理器")
@Component
public class CustomInstantiationProcessor implements InstantiationAwareBeanPostProcessor {

    public CustomInstantiationProcessor() {
        log.info("InstantiationAwareBeanPostProcessor 在其他 bean 建立前就建立完成");
    }

    /** 其他 bean 例項化之前呼叫 */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass.equals(SimpleBean.class)) {
            log.info("{} 即將例項化", beanName);
        }
        return null;
    }

    /** 其他 bean 例項化之後呼叫 */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (bean instanceof SimpleBean) {
            log.info("{} 例項化完成", beanName);
        }
        return true;
    }

}

初始化處理器

@Slf4j(topic = "自定義初始化處理器")
@Component
public class CustomInitializationProcessor implements BeanPostProcessor {

    public CustomInitializationProcessor() {
        log.info("BeanPostProcessor 在其他 bean 建立前就建立完成");
    }

    /** 其他 bean 初始化之前呼叫 */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SimpleBean) {
            log.info("{} 即將初始化", beanName);
        }
        return bean;
    }

    /** 其他 bean 初始化完成之後呼叫 */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SimpleBean) {
            log.info("{} 初始化完成", beanName);
        }
        return bean;
    }
}

銷燬處理器

@Slf4j(topic = "自定義銷燬處理器")
@Component
public class CustomDestructionProcessor implements DestructionAwareBeanPostProcessor {

    public CustomDestructionProcessor() {
        log.info("DestructionAwareBeanPostProcessor 在其他 bean 建立前就建立完成");
    }

    /** 其他 bean 銷燬之前呼叫 */
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (bean instanceof SimpleBean) {
            log.info("{} 即將銷燬", beanName);
        }
    }

}

入口類

@Slf4j
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    private SimpleBean bean;

    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public SimpleBean simpleBean() {
        return new SimpleBean();
    }

    @Autowired
    public void setBean(SimpleBean bean) {
        this.bean = bean;
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) {
        log.info("{} 使用", bean.toString());
    }

}

結果解析


  1. JSR 250 定義的 @PostConstruct、 @PreDestory 和 @Component 共同使用定義 bean 時,自定義的初始化函式會在 InitializingBean#afterPropertiesSet,擴充點之前執行,後續查閱原始碼研究。

相關文章