一、Bean 生命周期是 Spring 框架的"骨架"
理解 Spring 的 Bean 生命周期,不只是为了面试,更是为了理解 Spring 的扩展机制。Spring 的几乎所有高级特性——AOP、事务管理、事件机制、@Value 注入——都依赖于 Bean 生命周期中的特定回调点。如果不知道这些回调点在什么时候执行,你就无法理解为什么某些配置不生效、为什么某些注解不工作。
本文从源码视角(DefaultListableBeanFactory 和 AbstractAutowireCapableBeanFactory)逐阶段拆解 Bean 的生命周期。
二、完整生命周期概览
1. 实例化(Instantiation)
2. 属性填充(Populate)
3. Aware 回调(BeanNameAware -> BeanFactoryAware -> ApplicationContextAware)
4. BeanPostProcessor.postProcessBeforeInitialization()
5. 初始化回调(@PostConstruct -> InitializingBean.afterPropertiesSet() -> init-method)
6. BeanPostProcessor.postProcessAfterInitialization()
7. Bean 就绪(可用)
8. 销毁回调(@PreDestroy -> DisposableBean.destroy() -> destroy-method)
[图1:Bean生命周期完整流程图——从实例化到销毁的10个阶段,标注每个阶段的扩展点]

三、阶段一:实例化(Instantiation)
通过反射调用构造器创建 Bean 的原始实例。此时 Bean 的字段都是默认值,还没有注入任何依赖。Spring 通过 createBeanInstance() 方法完成实例化。实例化前扩展点:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。
四、阶段二:属性填充(Populate)
Spring 遍历 BeanDefinition 中的 PropertyValues 和注解信息(@Autowired、@Value、@Resource),通过反射将依赖注入到 Bean 中。@Autowired 和 @Value 的注入由 AutowiredAnnotationBeanPostProcessor 在这个阶段完成。
五、阶段三:Aware 回调
Aware 接口让 Bean 感知容器。执行顺序:BeanNameAware -> BeanClassLoaderAware -> BeanFactoryAware -> ApplicationContextAware。Aware 的陷阱:Aware 接口让你的 Bean 与 Spring 容器耦合。如果你不需要容器引用,不要实现 Aware 接口。
[图5:Aware接口体系与执行顺序图——BeanNameAware/BeanFactoryAware/ApplicationContextAware的继承关系]

六、阶段四:BeanPostProcessor 前置处理
BeanPostProcessor.postProcessBeforeInitialization() 在 Bean 的初始化方法(@PostConstruct)执行之前调用。CommonAnnotationBeanPostProcessor 在此阶段处理 @PostConstruct 和 @PreDestroy 注解。
七、阶段五:初始化回调
三种方式触发初始化回调,执行顺序:@PostConstruct(推荐,最简洁)-> InitializingBean.afterPropertiesSet()(耦合 Spring 接口)-> init-method(XML/@Bean 配置)。三个方法都会执行,建议只使用 @PostConstruct。
@Component
public class OrderService {
@PostConstruct
public void init() {
// 初始化缓存、注册监听器、预热数据
}
}
[图3:三种初始化回调对比图——@PostConstruct vs InitializingBean vs init-method的执行顺序和优缺点]

八、阶段六:BeanPostProcessor 后置处理
BeanPostProcessor.postProcessAfterInitialization() 在 Bean 初始化完成后执行。这是 Spring AOP 创建代理的时机——AbstractAutoProxyCreator 在这里检查 Bean 是否需要 AOP 代理(@Transactional、@Async),如果需要则创建 JDK 动态代理或 CGLIB 代理。Spring 容器中最终持有的 Bean 引用就是这个代理对象。
[图4:BeanPostProcessor前置/后置处理时机图——标注AOP代理创建在postProcessAfterInitialization阶段]

九、阶段七:Bean 就绪与销毁
Bean 经历以上所有阶段后,最终被放入 singleton 缓存(DefaultSingletonBeanRegistry.singletonObjects),可以被应用程序使用。容器关闭时执行销毁回调:@PreDestroy -> DisposableBean.destroy() -> destroy-method。
十、关键源码类
| 类 | 职责 |
|---|---|
| DefaultListableBeanFactory | Bean 注册和获取 |
| AbstractAutowireCapableBeanFactory | Bean 创建核心逻辑(doCreateBean) |
| AbstractAutoProxyCreator | AOP 代理创建 |
| AutowiredAnnotationBeanPostProcessor | @Autowired 注入 |
| CommonAnnotationBeanPostProcessor | @PostConstruct/@PreDestroy |
[图2:Bean生命周期关键源码类关系图——DefaultListableBeanFactory→AbstractAutowireCapableBeanFactory→doCreateBean以及各BeanPostProcessor]

[图6:Bean生命周期与Spring扩展点全景图——从启动到销毁的完整时间线,标注所有可扩展点]

十一、面试常见误区
- "BeanPostProcessor 在 Bean 创建后执行" —— 不准确。postProcessBeforeInitialization 在初始化前,postProcessAfterInitialization 在初始化后,AOP 代理在后者中创建。
- "Aware 接口是必须的" —— 不是。Aware 接口让你的 Bean 感知容器,但大多数情况下不需要。
- "三个初始化方法都会执行" —— 是的,按顺序。建议只使用 @PostConstruct。
- "Bean 实例化后就可以使用了" —— 不对。还需要属性填充、初始化和 AOP 代理创建后 Bean 才真正就绪。
十二、总结
Bean 生命周期是 Spring 框架的"骨架":实例化创建原始对象 -> 属性填充注入依赖 -> Aware 回调感知容器 -> BeanPostProcessor 前置处理 -> 初始化回调 -> BeanPostProcessor 后置处理(AOP 代理)-> Bean 就绪 -> 销毁回调。理解它就能理解 Spring 的扩展机制。
延伸阅读
- Spring 源码:AbstractAutowireCapableBeanFactory.doCreateBean()
- 《Spring 实战》第 2 章
- 本系列相关篇目:081(Spring IoC 与 DI)、083(BeanDefinition 与容器刷新)、087(Spring AOP 原理)
面试官视角:提问与回答
以下问题从面试官视角出发,考察你对 Bean 生命周期的深度理解和实战经验。
Q1:你在 Bean 的 @PostConstruct 方法中依赖了另一个 Bean B,但 B 的 @PostConstruct 还没有执行,导致出现了 NPE。这说明什么?怎么解决?
这说明两个 Bean 的初始化顺序不正确——Spring 在创建 Bean A 时,注入的 Bean B 可能还没完成初始化(@PostConstruct 还没执行)。这通常发生在循环依赖或依赖链不明确的情况下。解决方案:1)使用 @DependsOn("beanB") 注解显式指定依赖顺序。2)将初始化逻辑从 @PostConstruct 移到更早的时机——BeanPostProcessor.postProcessBeforeInitialization 或直接通过构造器参数传递已初始化的依赖。3)如果初始化逻辑互相依赖,考虑将初始化逻辑提取到独立的 InitializingBean 或 ApplicationListener 中,按顺序触发。4)更好的设计是将初始化逻辑从 Bean 中移出——使用 ApplicationRunner 或 CommandLineRunner 在容器启动完成后执行初始化。
Q2:你自定义了一个 BeanPostProcessor,在其中修改了 Bean 的属性。但你的 BeanPostProcessor 本身也需要被注入依赖,这个依赖在 BeanPostProcessor 的 postProcessBeforeInitialization 中可能还没有初始化完成。BeanPostProcessor 自己是何时被初始化的?
BeanPostProcessor 本身也是 Bean,但它的实例化和初始化比普通 Bean 更早。Spring 容器在 refresh() 过程中,通过 registerBeanPostProcessors() 方法专门处理 BeanPostProcessor 的注册:1)Spring 先创建所有实现了 PriorityOrdered 接口的 BeanPostProcessor。2)再创建所有实现了 Ordered 接口的 BeanPostProcessor。3)最后创建普通的 BeanPostProcessor。4)创建过程包括实例化、属性填充和初始化——所以 BeanPostProcessor 的依赖和 @PostConstruct 会正常执行。但需要注意:如果 BeanPostProcessor 依赖了某个普通 Bean,而这个普通 Bean 的创建又依赖于 BeanPostProcessor 的处理,就形成了循环依赖。解决方案:让 BeanPostProcessor 实现 BeanFactoryAware,通过 BeanFactory 手动获取依赖,而不是通过 @Autowired 注入。
Q3:你在 Bean 的 @PostConstruct 中启动了一个线程执行异步任务,但容器关闭时这个线程没有被停止。@PreDestroy 能保证线程被停止吗?
@PreDestroy 只在容器正常关闭时执行(调用 context.close() 或 JVM 收到 SIGTERM)。如果 JVM 被 kill -9 强制终止,@PreDestroy 不会执行。此外,@PreDestroy 中停止线程时,线程可能正在执行阻塞操作(如 Socket.accept()),导致线程无法立即停止。解决方案:1)使用线程池的 shutdown() + awaitTermination() 等待线程优雅停止。2)将异步任务委托给 Spring 的 TaskExecutor(@Async),Spring 会自动管理线程池的生命周期。3)注册 JVM ShutdownHook(Runtime.getRuntime().addShutdownHook())作为兜底。4)在 @PreDestroy 中设置超时——如果线程在超时后仍未停止,记录日志并强制退出。
Q4:Spring 的 AbstractAutoProxyCreator 在 postProcessAfterInitialization 阶段创建 AOP 代理。如果 Bean 实现了多个接口,Spring 会创建 JDK 动态代理还是 CGLIB 代理?如果 Bean 没有实现任何接口呢?
如果 Bean 实现了至少一个接口,且 proxyTargetClass 为 false(默认),Spring 使用 JDK 动态代理——代理对象实现了与目标 Bean 相同的接口。如果 Bean 没有实现任何接口,或 proxyTargetClass 为 true,Spring 使用 CGLIB 代理——通过继承目标 Bean 的类来创建代理。Spring Boot 2.0+ 默认 proxyTargetClass = true(使用 CGLIB),因为 CGLIB 更通用(不需要接口)。JDK 代理的优点是更轻量,CGLIB 代理的优点是更通用。在 Spring AOP 中,代理对象存放在容器中,调用 getBean() 获取的是代理对象,而不是原始 Bean。
Q5:你在 Bean 的构造器中调用了 @Autowired 注入的依赖,但依赖为 null。但在 @PostConstruct 中可以正常使用。为什么?构造器执行时依赖在什么时候被注入?
Bean 的构造器执行时,依赖还没有被注入——构造器执行是实例化阶段(阶段一),依赖注入是属性填充阶段(阶段二)。实例化阶段只创建了原始对象,Bean 的字段都是默认值。属性填充阶段才通过反射调用 setter 或直接设置字段来注入依赖。这就是为什么构造器注入是推荐的方式——依赖通过构造器参数传入,构造器执行时依赖就已经可用了。如果你使用字段注入,构造器执行时依赖为 null,只有在 @PostConstruct 中才能使用。