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

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

    return wrappedBean;
}

三级缓存机制详解

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

缓存级别数据结构作用何时写入何时读取
一级缓存 singletonObjectsConcurrentHashMap存放完全初始化好的单例 Bean初始化完成后任何 getBean() 调用
二级缓存 earlySingletonObjectsHashMap存放提前暴露的早期引用(半成品 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 代理时机、自调用失效

面试话术示例:当被问到"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 源码深度解析》

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。