Skip to content

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
  1. 实例化(Instantiation) — 通过构造器反射创建 Bean 实例。如果配置了 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),可以在这里返回代理对象替换默认实例化流程。Spring 内部默认使用 SimpleInstantiationStrategy,通过 ConstructorResolver 自动选择最优构造器(按参数最多的 public 构造器优先,没有则默认无参构造器)。

  2. 属性赋值(Populate) — 填充 @Autowired@Value、XML 中 <property> 等依赖。依赖注入的核心逻辑在这里,AutowiredAnnotationBeanPostProcessor 在此阶段执行字段注入。如果 Bean 有循环依赖,此时会触发三级缓存机制。

  3. Aware 回调 — 注入容器相关接口:BeanNameAware.setBeanName()BeanClassLoaderAware.setBeanClassLoader()BeanFactoryAware.setBeanFactory()ApplicationContextAware.setApplicationContext()(仅 ApplicationContext 环境)。这些接口的调用顺序固定,由 invokeAwareMethods() 方法统一处理。

  4. BeanPostProcessor 前置处理 — 调用所有注册的 BeanPostProcessor.postProcessBeforeInitialization()。这里可以做属性覆盖、代理对象包装等。注意:BeanPostProcessor 的排序由 PriorityOrderedOrdered → 无序 三级优先级控制

  5. 初始化(Initialization) — 按顺序执行:@PostConstruct 注解方法 → InitializingBean.afterPropertiesSet() → 自定义 init-method@PostConstruct 实际上由 CommonAnnotationBeanPostProcessorpostProcessBeforeInitialization 阶段触发,严格来说属于第 4 步,但效果上等价于初始化阶段。

  6. BeanPostProcessor 后置处理 — 调用 postProcessAfterInitialization()AOP 代理在此阶段生成AbstractAutoProxyCreator 检查 Bean 是否需要切面,需要则返回代理对象替换原始 Bean。

  7. 销毁(Destruction) — 容器关闭时按顺序执行:@PreDestroyDisposableBean.destroy() → 自定义 destroy-method。容器关闭通过 doClose()destroyBeans()disposableBeanAdapter.destroy() 调用链完成。

java
// 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. 初始化(afterPropertiesSet → init-method)
    invokeInitMethods(beanName, wrappedBean, mbd);

    // 4d. BeanPostProcessor 后置处理(AOP 代理在此,@Transactional 等注解生效)
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    return wrappedBean;
}

三级缓存机制详解

Spring 用三级缓存解决 setter 循环依赖,三级缓存分别对应:

缓存级别数据结构作用何时写入何时读取
一级缓存 singletonObjectsConcurrentHashMap存放完全初始化好的单例 Bean初始化完成后任何 getBean() 调用
二级缓存 earlySingletonObjectsConcurrentHashMap存放提前暴露的早期引用(半成品 Bean)三级缓存 lambda 执行后循环依赖发生时
三级缓存 singletonFactoriesHashMap存放 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() 中生成,具体实现是 AbstractAutoProxyCreatorSmartInstantiationAwareBeanPostProcessor 的子类)。流程如下:

  • 检查 Bean 上是否有 @Aspect 切面匹配的 @Before@Around 等切点
  • 匹配则创建代理对象(JDK 动态代理或 CGLIB),返回代理对象替换原始 Bean
  • 不匹配则返回原始 Bean 本身
java
// 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)。

java
// 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 代理时机、自调用失效

一句话概括 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 源码深度解析》

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1