Spring Bean 生命周期(从 XML 解析到销毁回调)
提出问题
Spring 框架中,一个 Bean 从配置到销毁的完整调用链是面试中绕不开的经典题。面试官通常不会只让你背"实例化 → 属性赋值 → 初始化 → 销毁"这四步,而是追问:AOP 代理在哪一步生成的?BeanPostProcessor 自己怎么处理生命周期?循环依赖发生时初始化阶段怎么绕过?生产上排查 Bean 初始化失败应该看哪个日志?这道题的核心不是背流程,而是理解 Spring IoC 容器在 doCreateBean() 方法中怎么一步步把一段配置或注解变成可用的代理对象。
分析问题
完整的 7 阶段生命周期
Spring Bean 的完整生命周期可以拆成 7 个阶段,每个阶段在 AbstractAutowireCapableBeanFactory.doCreateBean() 方法中都有对应的代码入口:
时序图(文字描述):
XML/注解配置 → BeanDefinition 注册 →
doCreateBean() 开始:
① 实例化(createBeanInstance)
② 提前暴露到三级缓存(addSingletonFactory)
③ 属性赋值(populateBean)
④ Aware 回调 → @PostConstruct → InitializingBean → init-method
⑤ BeanPostProcessor 前置处理
⑥ 初始化方法执行
⑦ BeanPostProcessor 后置处理(AOP 代理在此)
⑧ 注册销毁回调
→ 放入一级缓存(singletonObjects)
→ 返回完整 Bean实例化(Instantiation) — 通过构造器反射创建 Bean 实例。如果配置了
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),可以在这里返回代理对象替换默认实例化流程。Spring 内部默认使用SimpleInstantiationStrategy,通过ConstructorResolver自动选择最优构造器(按参数最多的 public 构造器优先,没有则默认无参构造器)。属性赋值(Populate) — 填充
@Autowired、@Value、XML 中<property>等依赖。依赖注入的核心逻辑在这里,AutowiredAnnotationBeanPostProcessor在此阶段执行字段注入。如果 Bean 有循环依赖,此时会触发三级缓存机制。Aware 回调 — 注入容器相关接口:
BeanNameAware.setBeanName()、BeanClassLoaderAware.setBeanClassLoader()、BeanFactoryAware.setBeanFactory()、ApplicationContextAware.setApplicationContext()(仅 ApplicationContext 环境)。这些接口的调用顺序固定,由invokeAwareMethods()方法统一处理。BeanPostProcessor 前置处理 — 调用所有注册的
BeanPostProcessor.postProcessBeforeInitialization()。这里可以做属性覆盖、代理对象包装等。注意:BeanPostProcessor 的排序由PriorityOrdered→Ordered→ 无序 三级优先级控制。初始化(Initialization) — 按顺序执行:
@PostConstruct注解方法 →InitializingBean.afterPropertiesSet()→ 自定义init-method。@PostConstruct实际上由CommonAnnotationBeanPostProcessor在postProcessBeforeInitialization阶段触发,严格来说属于第 4 步,但效果上等价于初始化阶段。BeanPostProcessor 后置处理 — 调用
postProcessAfterInitialization()。AOP 代理在此阶段生成:AbstractAutoProxyCreator检查 Bean 是否需要切面,需要则返回代理对象替换原始 Bean。销毁(Destruction) — 容器关闭时按顺序执行:
@PreDestroy→DisposableBean.destroy()→ 自定义destroy-method。容器关闭通过doClose()→destroyBeans()→disposableBeanAdapter.destroy()调用链完成。
// AbstractAutowireCapableBeanFactory.doCreateBean() 关键流程(简化)
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1. 实例化
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
// 2. 提前暴露(循环依赖三级缓存)
// 如果允许循环依赖且是单例,在属性赋值前就暴露早期引用
// 这一步是三级缓存的核心:lambda 表达式在 getEarlyBeanReference 时才执行
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. 属性赋值(含循环依赖处理)
populateBean(beanName, mbd, instanceWrapper);
// 4. 初始化(含 Aware → Before → init → After)
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 4a. Aware 回调(BeanNameAware → BeanClassLoaderAware → BeanFactoryAware)
invokeAwareMethods(beanName, bean);
// 4b. BeanPostProcessor 前置处理(@PostConstruct 在此被 CommonAnnotationBeanPostProcessor 触发)
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 4c. 初始化(@PostConstruct → afterPropertiesSet → init-method)
invokeInitMethods(beanName, wrappedBean, mbd);
// 4d. BeanPostProcessor 后置处理(AOP 代理在此,@Transactional 等注解生效)
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}三级缓存机制详解
Spring 用三级缓存解决 setter 循环依赖,三级缓存分别对应:
| 缓存级别 | 数据结构 | 作用 | 何时写入 | 何时读取 |
|---|---|---|---|---|
一级缓存 singletonObjects | ConcurrentHashMap | 存放完全初始化好的单例 Bean | 初始化完成后 | 任何 getBean() 调用 |
二级缓存 earlySingletonObjects | HashMap | 存放提前暴露的早期引用(半成品 Bean) | 三级缓存 lambda 执行后 | 循环依赖发生时 |
三级缓存 singletonFactories | HashMap | 存放 ObjectFactory lambda 工厂 | 实例化后、属性赋值前 | 循环依赖首次发现时 |
为什么是三级不是两级? 因为 AOP 代理需要延迟生成。如果 Bean 不需要 AOP 增强,getEarlyBeanReference() 直接返回原始 Bean,不需要提前创建代理。如果直接存二级缓存,每个 Bean 实例化后都要执行一次 AOP 判断,浪费性能。三级缓存通过 lambda 实现了 懒加载 + 定制化:只有确实发生循环依赖需要提前暴露时,才执行 getEarlyBeanReference()。Spring 官方注释里写得很清楚:"this is an internal usage"。
三级缓存性能影响:在 1000+ Bean 的微服务中,三级缓存带来的额外内存开销约为 8KB × Bean 数(每个 ObjectFactory lambda 约 4 个引用 + 闭包变量),对 8GB 堆来说可以忽略不计。
AOP 代理的生成时机
AOP 代理在 postProcessAfterInitialization() 中生成,具体实现是 AbstractAutoProxyCreator(SmartInstantiationAwareBeanPostProcessor 的子类)。流程如下:
- 检查 Bean 上是否有
@Aspect切面匹配的@Before、@Around等切点 - 匹配则创建代理对象(JDK 动态代理或 CGLIB),返回代理对象替换原始 Bean
- 不匹配则返回原始 Bean 本身
// AbstractAutoProxyCreator 简化逻辑
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取当前 Bean 的匹配 Advisors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != null) {
// 创建代理:JDK 动态代理(有接口)或 CGLIB(无接口)
bean = createProxy(bean.getClass(), beanName, specificInterceptors, bean);
}
}
return bean;
}这就意味着:@Transactional 和 @Cacheable 这类需要 AOP 增强的注解,在 Bean 初始化完成后才能生效。内部方法自调用无效的原因也在这里——this.method() 指向的是原始 Bean 对象,不是代理对象。
生产故障案例:某团队在 @PostConstruct 中调用本类 @Transactional 方法,发现事务始终不生效。排查后发现是自调用问题——@PostConstruct 执行时 Bean 尚未被代理对象包裹,this.method() 走的是原始对象。解决方案:注入自己的代理对象(@Autowired self),或者将事务方法提取到另一个 Service Bean 中调用。
BeanPostProcessor 自身的生命周期
BeanPostProcessor 本身也是一个 Bean,那它的生命周期怎么处理?答案是:BeanPostProcessor 的实例化和初始化比普通 Bean 更早。
在 AbstractApplicationContext.refresh() 的 registerBeanPostProcessors() 阶段,Spring 会先实例化所有实现了 BeanPostProcessor 接口的 Bean,保证它们在其他普通 Bean 初始化之前就绪。这些 BeanPostProcessor 的实例化采用 getBean() 调用,同样走 doCreateBean() 流程,但由于此时还没有其他 BeanPostProcessor 注册,它们自身不会触发 postProcessBefore/After initialization 回调(或者说只能用已经注册的、优先级更高的 BeanPostProcessor)。
// AbstractApplicationContext.refresh() 中相关步骤
// 步骤 5: 调用 BeanFactoryPostProcessor(处理 @Configuration 等配置类)
invokeBeanFactoryPostProcessors(beanFactory);
// 步骤 6: 注册 BeanPostProcessor(此时实例化所有 BPP)
// 顺序:PriorityOrdered → Ordered → 普通
registerBeanPostProcessors(beanFactory);
// 步骤 7-11: 初始化 MessageSource、ApplicationEventMulticaster 等基础设施
// ...
// 步骤 12: 实例化所有非懒加载单例 Bean(走完整的 doCreateBean 生命周期)
finishBeanFactoryInitialization(beanFactory);BeanPostProcessor 排序问题:如果一个自定义 BeanPostProcessor 没有实现 Ordered 接口,它的优先级是最低的,但执行顺序可能不稳定。生产上曾遇到 @Transactional 在某些 Bean 上不生效的问题,排查发现是自定义 BeanPostProcessor 在 AbstractAutoProxyCreator 之前执行,并提前返回了代理对象,导致 AbstractAutoProxyCreator 跳过处理。修复方式:让自定义 BPP 实现 Ordered 并设置低优先级,或者在 postProcessBeforeInitialization 中不做代理返回。
循环依赖与生命周期的交互
当发生 setter 循环依赖时,Bean 的生命周期会被"打断":A 实例化后放入三级缓存 → 填充属性时发现依赖 B → 暂停 A 的初始化,先去创建 B → B 填充属性时从三级缓存拿到 A 的早期引用 → B 继续完成初始化 → A 从三级缓存取出,继续完成属性赋值和初始化。关键是:A 的 postProcessAfterInitialization() 不会再次执行,因为早期引用已经通过 getEarlyBeanReference() 调用了 AOP 代理逻辑。
setter 循环依赖详细时序:
1. getBean(A) → doCreateBean(A) 开始
2. createBeanInstance(A) → A 实例化完成
3. addSingletonFactory(A, factory) → A 的早期引用暴露到三级缓存
4. populateBean(A) → 发现需要注入 B
5. getBean(B) → doCreateBean(B) 开始
6. createBeanInstance(B) → B 实例化完成
7. addSingletonFactory(B, factory) → B 暴露到三级缓存
8. populateBean(B) → 发现需要注入 A
9. getBean(A) → 三级缓存中拿到 A 的早期引用(并移入二级缓存)
10. B 的 A 依赖注入完成 → 继续 populateBean(B)
11. initializeBean(B) → B 初始化完成 → 放入一级缓存
12. 回到 A 的 populateBean → A 的 B 依赖注入完成
13. initializeBean(A) → A 初始化完成
14. A 放入一级缓存,移除二/三级缓存中的 A注意:构造器注入不支持循环依赖。因为构造器在 createBeanInstance() 阶段就需要依赖,此时还没有暴露到三级缓存。当构造器循环依赖发生时,Spring 会抛出 BeanCurrentlyInCreationException。生产上遇到这种情况,要么改成 setter 注入,要么用 @Lazy 延迟加载其中一个依赖。
总结
把 Bean 生命周期理清楚,核心是记住这 3 个关键点:
| 阶段 | 关键方法 | 作用 | 面试常问 |
|---|---|---|---|
| 实例化 | createBeanInstance() | 反射创建,可被 InstantiationAwareBeanPostProcessor 拦截 | 构造器注入 vs setter 注入 |
| 属性赋值 | populateBean() | 注入依赖,循环依赖的三级缓存在此介入 | 三级缓存为什么是三级 |
| 初始化 | initializeBean() | Aware → Before → @PostConstruct → afterPropertiesSet → init-method → After(AOP 代理) | AOP 代理时机、自调用失效 |
面试话术示例:当被问到"Bean 初始化过程中 AOP 代理什么时候创建"时,可以这样答:"AOP 代理在 postProcessAfterInitialization 中生成,这是初始化阶段的最后一步。AbstractAutoProxyCreator 会检查 Bean 是否匹配切面,匹配则生成代理对象替换原始 Bean。所以 @Transactional 在 Bean 初始化完成后才生效,自调用无效的原因也在这里——内部方法调用走的是原始 Bean 而非代理。但需要注意,如果发生循环依赖,AOP 代理会在三级缓存的 getEarlyBeanReference() 中提前生成,不会等到 postProcessAfterInitialization。"
生产避坑清单:
@PostConstruct中调@Transactional自调用 → 事务不生效,因为此时 Bean 还未被代理- 自定义 BeanPostProcessor 未实现
Ordered→ 可能与AbstractAutoProxyCreator执行顺序冲突,导致某些 Bean 的 @Transactional 失效 - 构造器循环依赖 → 直接抛
BeanCurrentlyInCreationException,必须改成 setter 注入或用@Lazy - Bean 初始化阶段抛异常(比如
@PostConstruct中调了外部 RPC)→ Spring 启动时抛出BeanCreationException,排查时通过--debug参数可以看到具体哪个 Bean 的哪个阶段失败 ApplicationContextAware注入的 ApplicationContext 不要存为静态变量 → 单测环境多 ApplicationContext 时会互相污染
参考:Spring 源码 — AbstractAutowireCapableBeanFactory.doCreateBean()、AbstractAutoProxyCreator.postProcessAfterInitialization()、DefaultSingletonBeanRegistry 三级缓存实现;《Spring 源码深度解析》