Spring Bean 的生命周期详解
一、什么是 Bean 的生命周期?
Bean 的生命周期是指从 Spring 容器创建 Bean 实例开始,到最终销毁 Bean 的整个过程。理解这个过程,能帮你搞清楚很多问题:为什么 @PostConstruct 能在依赖注入完成后执行、AOP 代理是在哪个阶段生成的、@PreDestroy 为什么有时候不生效。
二、完整生命周期流程
Spring Bean 的生命周期可以概括为四个大阶段:实例化 → 属性填充 → 初始化 → 销毁。但在这四个阶段之间,穿插着多个扩展点。
┌─────────────────────────────────────────────────────────────────┐
│ Bean 生命周期完整流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ① 实例化(Instantiation) │
│ └── 通过反射调用构造方法创建对象 │
│ │
│ ② 属性填充(Populate) │
│ └── 依赖注入(@Autowired、@Value) │
│ │
│ ③ Aware 接口回调 │
│ ├── BeanNameAware.setBeanName() │
│ ├── BeanFactoryAware.setBeanFactory() │
│ ├── ApplicationContextAware.setApplicationContext() │
│ └── 其他 Aware 接口 │
│ │
│ ④ BeanPostProcessor.postProcessBeforeInitialization() │
│ └── 初始化前增强 │
│ │
│ ⑤ 初始化(Initialization) │
│ ├── @PostConstruct 标注的方法 │
│ ├── InitializingBean.afterPropertiesSet() │
│ └── init-method 或 @Bean(initMethod = "...") │
│ │
│ ⑥ BeanPostProcessor.postProcessAfterInitialization() │
│ └── 初始化后增强(AOP 代理在此生成) │
│ │
│ ⑦ Bean 就绪,可以使用 │
│ │
│ ⑧ 容器关闭,销毁 Bean │
│ ├── @PreDestroy 标注的方法 │
│ ├── DisposableBean.destroy() │
│ └── destroy-method 或 @Bean(destroyMethod = "...") │
│ │
└─────────────────────────────────────────────────────────────────┘
三、各阶段详解
3.1 实例化(Instantiation)
Spring 通过反射调用构造方法创建 Bean 实例。如果使用构造器注入,依赖在此时作为参数传入。如果使用字段注入,此时对象已创建,但依赖字段还是 null。
java
@Service
public class UserService {
@Autowired
private UserDao userDao; // 此时还是 null
public UserService() {
System.out.println("1. 构造方法执行,userDao = " + userDao); // null
}
}
3.2 属性填充(Populate)
Spring 扫描 Bean 中标注了 @Autowired、@Value、@Resource 的字段和方法,从容器中查找匹配的依赖并注入。
java
@Autowired
private UserDao userDao; // 此时被注入
@Value("${app.name}")
private String appName; // 此时被注入
如果依赖是循环依赖,Spring 通过三级缓存提前暴露对象的早期引用,让依赖方先拿到引用。
3.3 Aware 接口回调
如果 Bean 实现了 Aware 系列接口,Spring 会调用对应的方法,让 Bean 获取容器内部的一些资源。
| Aware 接口 | 回调方法 | 获取的资源 |
|---|---|---|
BeanNameAware |
setBeanName(String) |
Bean 的名称 |
BeanFactoryAware |
setBeanFactory(BeanFactory) |
BeanFactory 实例 |
ApplicationContextAware |
setApplicationContext(ApplicationContext) |
ApplicationContext 实例 |
EnvironmentAware |
setEnvironment(Environment) |
Environment 实例 |
ResourceLoaderAware |
setResourceLoader(ResourceLoader) |
资源加载器 |
ApplicationEventPublisherAware |
setApplicationEventPublisher(...) |
事件发布器 |
java
@Component
public class MyBean implements BeanNameAware, ApplicationContextAware {
@Override
public void setBeanName(String name) {
System.out.println("3. Bean 名称: " + name);
}
@Override
public void setApplicationContext(ApplicationContext context) {
System.out.println("3. 获取到 ApplicationContext");
}
}
3.4 BeanPostProcessor 前置处理
BeanPostProcessor 是 Spring 最重要的扩展点之一。它的 postProcessBeforeInitialization 方法会在所有初始化方法执行之前被调用。
java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("4. 初始化前: " + beanName);
return bean; // 返回 null 则不会继续后续处理
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("6. 初始化后: " + beanName);
return bean;
}
}
如果返回 null,Spring 不会继续执行后续的初始化流程,Bean 也不会被注册到容器中。
3.5 初始化(Initialization)
初始化阶段按以下顺序执行:
1. @PostConstruct 标注的方法
java
@PostConstruct
public void init() {
System.out.println("5.1 @PostConstruct 执行");
}
2. InitializingBean.afterPropertiesSet()
java
@Component
public class MyBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("5.2 afterPropertiesSet 执行");
}
}
3. init-method 或 @Bean(initMethod = "...")
java
@Bean(initMethod = "customInit")
public MyBean myBean() {
return new MyBean();
}
// MyBean 中定义 customInit 方法
public void customInit() {
System.out.println("5.3 init-method 执行");
}
执行顺序:@PostConstruct → afterPropertiesSet() → init-method
3.6 BeanPostProcessor 后置处理
postProcessAfterInitialization 在所有初始化方法执行之后被调用。AOP 代理就是在这个阶段生成的。
java
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 如果 Bean 需要被 AOP 代理,这里返回代理对象
// Spring 的 AbstractAutoProxyCreator 就是在这里创建代理的
return bean;
}
如果 Bean 被 AOP 代理,容器中最终存放的是代理对象,而不是原始对象。这也解释了为什么 @Transactional 注解的方法必须通过代理调用才生效。
3.7 Bean 就绪
经过以上所有阶段后,Bean 被放入单例池(singletonObjects),可以被应用程序使用。
3.8 销毁(Destruction)
容器关闭时,Bean 的销毁流程按以下顺序执行:
1. @PreDestroy 标注的方法
java
@PreDestroy
public void preDestroy() {
System.out.println("8.1 @PreDestroy 执行");
}
2. DisposableBean.destroy()
java
@Component
public class MyBean implements DisposableBean {
@Override
public void destroy() {
System.out.println("8.2 destroy 执行");
}
}
3. destroy-method 或 @Bean(destroyMethod = "...")
java
@Bean(destroyMethod = "customDestroy")
public MyBean myBean() {
return new MyBean();
}
执行顺序:@PreDestroy → destroy() → destroy-method
四、完整示例
java
@Component
public class LifecycleBean implements BeanNameAware, InitializingBean, DisposableBean {
@Autowired
private UserDao userDao;
public LifecycleBean() {
System.out.println("1. 构造方法执行");
}
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware.setBeanName: " + name);
}
@PostConstruct
public void postConstruct() {
System.out.println("5.1 @PostConstruct 执行");
}
@Override
public void afterPropertiesSet() {
System.out.println("5.2 afterPropertiesSet 执行");
}
public void customInit() {
System.out.println("5.3 init-method 执行");
}
@PreDestroy
public void preDestroy() {
System.out.println("8.1 @PreDestroy 执行");
}
@Override
public void destroy() {
System.out.println("8.2 destroy 执行");
}
public void customDestroy() {
System.out.println("8.3 destroy-method 执行");
}
}
配置:
java
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleBean lifecycleBean() {
return new LifecycleBean();
}
输出:
1. 构造方法执行
2. 属性填充(依赖注入)
3. BeanNameAware.setBeanName: lifecycleBean
4. BeanPostProcessor.postProcessBeforeInitialization
5.1 @PostConstruct 执行
5.2 afterPropertiesSet 执行
5.3 init-method 执行
6. BeanPostProcessor.postProcessAfterInitialization
7. Bean 就绪
... 应用运行 ...
8.1 @PreDestroy 执行
8.2 destroy 执行
8.3 destroy-method 执行
五、作用域对生命周期的影响
| 作用域 | 生命周期 |
|---|---|
singleton |
容器启动时创建,容器关闭时销毁 |
prototype |
每次获取时创建,Spring 不管理销毁(需要调用方自行处理) |
request |
每次 HTTP 请求创建,请求结束时销毁 |
session |
每个会话创建,会话失效时销毁 |
application |
整个 ServletContext 生命周期 |
prototype 作用域的 Bean,Spring 只负责创建,不负责销毁。@PreDestroy 和 DisposableBean 不会生效。
六、生命周期中的扩展点
| 扩展点 | 类型 | 作用 |
|---|---|---|
BeanPostProcessor |
接口 | 在初始化前后增强 Bean |
InstantiationAwareBeanPostProcessor |
接口 | 在实例化前后增强 Bean |
@PostConstruct |
注解 | 初始化方法 |
InitializingBean |
接口 | 初始化方法 |
init-method |
XML/注解 | 初始化方法 |
@PreDestroy |
注解 | 销毁方法 |
DisposableBean |
接口 | 销毁方法 |
destroy-method |
XML/注解 | 销毁方法 |
七、常见问题
1. @PostConstruct 在依赖注入完成后才执行
这个顺序保证了 @PostConstruct 方法中可以直接使用注入的依赖,不会出现空指针。
java
@PostConstruct
public void init() {
userDao.count(); // 安全,userDao 已经被注入
}
2. AOP 代理在哪个阶段生成?
AOP 代理在 BeanPostProcessor.postProcessAfterInitialization() 中生成。所以如果 Bean 被 AOP 代理,容器中存放的是代理对象。这也解释了为什么同一个类内部方法调用不会触发 AOP------因为内部调用走的是原始对象,不是代理对象。
3. 构造方法中为什么不能使用注入的依赖?
构造方法执行时,属性填充还没开始。所以构造方法中 userDao 还是 null。如果需要在构造方法中使用依赖,应使用构造器注入。
4. prototype Bean 的销毁方法为什么不执行?
Spring 只负责创建 prototype Bean,创建完成后就不再管理它的生命周期。销毁方法需要调用方手动调用,或者通过 DisposableBeanAdapter 自行处理。
5. BeanPostProcessor 为什么能拦截所有 Bean?
BeanPostProcessor 本身也是一个 Bean,但它的注册时机非常早(在 refresh() 的 registerBeanPostProcessors() 阶段)。Spring 在创建任何 Bean 时,都会先获取所有已注册的 BeanPostProcessor,依次调用它们的后置处理方法。
八、总结
Bean 的生命周期可以概括为四个核心阶段:实例化 → 属性填充 → 初始化 → 销毁。在这四个阶段之间,Spring 提供了多个扩展点,让开发者可以在合适的时机插入自定义逻辑。
| 阶段 | 核心操作 | 常用扩展点 |
|---|---|---|
| 实例化 | 反射调用构造方法 | InstantiationAwareBeanPostProcessor |
| 属性填充 | 依赖注入 | @Autowired、@Value |
| Aware 回调 | 获取容器资源 | BeanNameAware、ApplicationContextAware |
| 初始化前 | BeanPostProcessor 前置 | postProcessBeforeInitialization |
| 初始化 | 执行初始化逻辑 | @PostConstruct、InitializingBean、init-method |
| 初始化后 | BeanPostProcessor 后置 | postProcessAfterInitialization(AOP 代理在此生成) |
| 使用 | Bean 就绪 | --- |
| 销毁 | 释放资源 | @PreDestroy、DisposableBean、destroy-method |
理解 Bean 生命周期,能帮你解释很多 Spring 中的"奇怪现象":为什么 @PostConstruct 能拿到注入的依赖、为什么 AOP 代理的方法调用有"自调用失效"问题、为什么 prototype Bean 的销毁方法不生效。掌握生命周期,就掌握了 Spring 容器管理的底层逻辑。