Spring Bean 的生命周期
- 2021 年 3 月 3 日
- 筆記
除了使用階段外,Spring 將 bean 的生命周期定義為實例化、屬性填充、初始化和銷毀四個階段,並為每個階段提供了多個拓展點用於自定義 bean 的創建過程。這篇文章介紹了 bean 的生命周期和其各個拓展點,通過圖示生動的展示、並結合一個實例來演示全過程。
Spring 生命周期
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
包的BeanPostProcessor
、InstantiationAwareBeanPostProcessor
和DestructionAwareBeanPostProcessor
介面,這些介面中定義了多個拓展點,使用時需要定義一個專門的類實現介面、重寫必要的函數。Spring 容器會將這些實現類優先註冊為 bean,待它們初始化完成後再初始化普通的 bean,在每個普通 bean 註冊時,Spring 容器都會嘗試調用所有已註冊的通用拓展點。classDiagram
BeanPostProcessor <|– InstantiationAwareBeanPostProcessor
BeanPostProcessor <|– DestructionAwareBeanPostProcessor
BeanPostProcessor: +postProcessBeforeInitialization()
BeanPostProcessor: +postProcessAfterInitialization()InstantiationAwareBeanPostProcessor: +postProcessBeforeInstantiation()
InstantiationAwareBeanPostProcessor: +postProcessAfterInstantiation()DestructionAwareBeanPostProcessor: +postProcessBeforeDestruction()
1. 實例化
創建 bean 對象實例的過程,包括使用工廠模式創建和調用構造函數。Spring 通過 InstantiationAwareBeanPostProcessor
介面在實例化前和後各提供了兩個通用拓展點,加上對象實例化的過程,執行順序如下:
postProcessBeforeInstantiation
:在普通 bean 對象實例化開始之前調用- 對象實例化
postProcessAfterInstantiation
:在普通 bean 對象實例化完成之後調用
2. 屬性填充
如果對象中有 setter 函數,並通過配置元數據指定了注入的屬性,Spring 容器會在這一步為其注入配置的值。完成屬性填充後,Spring 通過 Aware(意為感知) 介面提供了十個專用拓展點,這些拓展點用於在 bean 自身內部、感知一些外部資訊使用。調用順序如下(下面提到的多個 Aware 介面中,前三個在 org.springframework.beans.factory
包中,4 ~ 9 在 org.springframework.context
包中,最後一個在 org.springframework.web.context
包中 ):
BeanNameAware#setBeanName
BeanClassLoaderAware#setBeanClassLoader
BeanFactoryAware#setBeanFactory
EnvironmentAware#setEnvironment
EmbeddedValueResolverAware#setEmbeddedValueResolver
ResourceLoaderAware#setResourceLoader
(僅在 ApplicationContext 中有效)ApplicationEventPublisherAware#setApplicationEventPublisher
(僅在 ApplicationContext 中有效)MessageSourceAware#setMessageSource
(僅在 ApplicationContext 中有效)ApplicationContextAware#setApplicationContext
(僅在 ApplicationContext 中有效)- 如果是 Web 應用程式,還有
ServletContextAware#setServletContext
(僅在 WebApplicationContext 中有效)
Aware 拓展點結束之後,還有一個用於在初始化之前、進行屬性校驗的 InitializingBean#afterPropertiesSet
專用拓展點。
3. 初始化
初始化是指通過 bean 在將要工作前進行的最後準備工作,通常是 @Bean
的initMethod
屬性定義的函數執行的過程 。Spring 通過 BeanPostProcessor
介面在初始化之前和之後提供了兩個通用拓展點,加上初始化函數執行順序為:
postProcessBeforeInitialization
- 自定義的初始化函數
postProcessAfterInitialization
4. 銷毀
銷毀是指 bean 釋放其佔用的一些外部資源的過程,通常是 @Bean
註解的 destroyMethod
屬性定義的銷毀函數執行的過程。Spring 通過 DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
的通用拓展點,再加上 DisposableBean#destroy
提供了的專用拓展點,三者執行順序為:
DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
DisposableBean#destroy
- 自定義的銷毀函數。
實例演示
一個簡單的演示,實現了上文提到的 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());
}
}
結果解析
- JSR 250 定義的 @PostConstruct、 @PreDestory 和 @Component 共同使用定義 bean 時,自定義的初始化函數會在
InitializingBean#afterPropertiesSet
,拓展點之前執行,後續查閱源碼研究。