Spring IOC 容器管理 Bean 生命週期的能力是框架核心特性之一。理解完整流程、擴展點與執行順序,對於編寫健壯的 Spring 應用、排查初始化順序問題、實現自定義邏輯至關重要。本文基於 Spring 4.x/5.x 源碼,系統梳理 Bean 生命週期七大階段、擴展介面與實戰應用。
BeanNameAware、BeanFactoryAware、ApplicationContextAware 等postProcessBeforeInitializationInitializingBean.afterPropertiesSet() → @PostConstruct → init-methodpostProcessAfterInitialization (AOP 代理在此生成)DisposableBean.destroy() → @PreDestroy → destroy-method<bean id="user" class="com.example.User"
init-method="init" destroy-method="destroy"
scope="singleton">
<property name="userName" value="張三"/>
<property name="wifeName" value="#{girl.userName}"/> <!-- SpEL -->
</bean>
public class User {
private String userName;
private String wifeName;
// getter/setter 省略
public User() { System.out.println("1. 構造器執行"); }
// InitializingBean 介面方法
public void afterPropertiesSet() { System.out.println("4. afterPropertiesSet()"); }
// JSR-250 註解 (需啟用 CommonAnnotationBeanPostProcessor)
@PostConstruct
public void postConstruct() { System.out.println("5. @PostConstruct"); }
// XML 配置 init-method
public void init() { System.out.println("6. init-method"); }
// DisposableBean 介面方法
public void destroy() { System.out.println("9. destroy()"); }
@PreDestroy
public void preDestroy() { System.out.println("8. @PreDestroy"); }
// XML 配置 destroy-method
public void destroyMethod() { System.out.println("10. destroy-method"); }
}
1. 構造器執行
2. setUserName(張三)
3. setWifeName(張三) <-- SpEL 解析
4. afterPropertiesSet() <-- InitializingBean
5. @PostConstruct <-- JSR-250
6. init-method <-- XML init-method
7. postProcessAfterInitialization() <-- BeanPostProcessor (AOP 代理生成)
...
容器關閉:
8. @PreDestroy
9. destroy() <-- DisposableBean
10. destroy-method <-- XML destroy-method
| 機制 | 類型 | 執行順序 | 耦合度 | 推薦度 | 備註 |
|---|---|---|---|---|---|
InitializingBean / DisposableBean | 介面 | 1 / 1 | 高 (Spring 專用) | ⭐⭐ | 侵入業務代碼 |
@PostConstruct / @PreDestroy | 註解 (JSR-250) | 2 / 2 | 低 (標準規範) | ⭐⭐⭐⭐⭐ | 首選,需 javax.annotation 依賴 (JDK 9+ 模組化) |
init-method / destroy-method | XML 配置 | 3 / 3 | 零 (POJO) | ⭐⭐⭐⭐ | 第三方類無法加註解時 |
| 默認方法名 | 全域預設 | - | 零 | ⭐⭐⭐ | <beans default-init-method="init" default-destroy-method="destroy"> |
作用:在 Bean 初始化前後攔截、修改、替換、包裝 Bean 實例。對容器中所有 Bean 生效。
public class MyBeanPostProcessor implements BeanPostProcessor {
// 初始化前:可修改屬性、返回代理對象
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("user".equals(beanName)) {
System.out.println("Before init: " + beanName);
// 可返回不同對象 (如代理)
}
return bean; // 必須返回非 null
}
// 初始化後:AOP 代理在此生成、可進一步包裝
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("user".equals(beanName)) {
System.out.println("After init: " + beanName);
// 可修改已初始化的屬性
((User) bean).setUserName("修改後名稱");
}
return bean;
}
}
關鍵點:
null 會導致 getBean() 返回 nullAutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、AspectJAutoProxyCreator (AOP) 等作用:在 Bean 實例化前,讀取/修改 Bean 定義元數據 (BeanDefinition)。
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 修改 Bean 定義:scope、lazy-init、屬性值等
BeanDefinition bd = beanFactory.getBeanDefinition("user");
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd.getPropertyValues().add("userName", "工廠修改");
// 可註冊 BeanPostProcessor
beanFactory.addBeanPostProcessor(new MyBeanPostProcessor());
}
}
經典應用:PropertyPlaceholderConfigurer (屬性佔位符解析)、ConfigurationClassPostProcessor (@Configuration 類解析)
執行順序:BeanFactoryPostProcessor (容器啟動早期) → BeanPostProcessor (Bean 初始化前後)
繼承 BeanPostProcessor,增加實例化階段攔截:
postProcessBeforeInstantiation:實例化前,可返回代理對象跳過預設實例化 (AOP 核心)postProcessAfterInstantiation:實例化後,屬性填充前,返回 false 跳過屬性填充postProcessPropertyValues:屬性填充前,可修改屬性值建議繼承 InstantiationAwareBeanPostProcessorAdapter 適配器類,僅覆寫所需方法。
<bean id="dateFormat" class="java.text.DateFormat" factory-method="getDateInstance">
<constructor-arg value="2"/> <!-- 對應 getDateInstance(int style) -->
</bean>
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
</bean>
<bean id="datetime" factory-bean="simpleDateFormat" factory-method="parse">
<constructor-arg value="1990-12-12 12:12:12"/>
</bean>
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
user.setUserName("FactoryBean創建");
return user;
}
@Override public Class<?> getObjectType() { return User.class; }
@Override public boolean isSingleton() { return true; }
}
<bean id="user" class="com.example.UserFactoryBean"/>
關鍵差異:注入 user 獲得的是 User 實例而非 UserFactoryBean;若需注入工廠本身,使用 &user。
| Scope | 生命週期 | 創建時機 | 適用場景 |
|---|---|---|---|
| singleton (預設) | 容器啟動~關閉 | 容器啟動時 (非 lazy-init) | 無狀態服務、DAO、Controller |
| prototype | 每次 getBean 新建 | 每次請求時 | 有狀態物件、Struts2 Action |
| request | 單次 HTTP 請求 | 請求開始 | Web 請求級資料 |
| session | HTTP Session 週期 | Session 創建 | 用戶會話資料 |
| application | ServletContext 週期 | 應用啟動 | 全域快取、配置 |
| websocket | WebSocket 會話 | 連接建立 | WebSocket 狀態 |
Spring 通過三級快取解決單例 Bean 循環依賴 (僅支援 setter/field 注入):
singletonObjects:完全初始化好的 BeanearlySingletonObjects:早期暴露的 Bean (實例化完成、屬性未填充)singletonFactories:ObjectFactory 工廠,可生成早期引用 (AOP 代理)流程:A 實例化 → 放入三級快取 → 屬性填充需 B → B 實例化 → 放入三級快取 → 屬性填充需 A → 從三級快取獲取 A 早期引用 (觸發 AOP 代理) → B 完成 → A 完成 → 移至一級快取。
@PostConstruct,銷毀用 @PreDestroyinit-method/destroy-methodBeanFactoryPostProcessorBeanPostProcessorInstantiationAwareBeanPostProcessor核心心法:Bean 生命週期是 Spring 容器的心跳。掌握七大階段、三大擴展介面、三級快取機制,能讓你在面對複雜初始化順序、AOP 代理生成時機、循環依賴、第三方庫整合等問題時,從容定位根因並優雅解決。
建議動手調試:在 IDEA 中對 AbstractAutowireCapableBeanFactory.doCreateBean() 打斷點,單步跟蹤完整流程,加深理解。