夜雨聆风学习资料网

ARTICLE · 1326017

死磕 Spring 6.0 源码:一张流程图,彻底揭开 Bean 初始化与 AOP 的神秘面纱

死磕 Spring 6.0 源码:一张流程图,彻底揭开 Bean 初始化与 AOP 的神秘面纱

在 Java 后端面试中,Spring Bean 的生命周期几乎是必考题。很多候选人只能干巴巴地背出“实例化、属性赋值、初始化”这三个词。

但如果面试官追问:“Spring 是如何解决循环依赖的?”或者“Spring 的 AOP 代理到底是在哪个阶段生成的?”,大部分人就哑火了。

今天,我们结合 Spring 6.0 的源码,配合核心执行流程图,深入 Spring 容器的黑盒内部,彻底搞懂 Bean 从实例化到完成初始化的底层真相。

说明:以下代码为便于公众号阅读有简化,完整源码请参考 Spring 6.0 的 AbstractAutowireCapableBeanFactory。


01一、核心概念辨析:实例化 vs 属性赋值 vs 初始化

在深入源码前,必须厘清三个极易混淆的概念:

1. 实例化(Instantiation)

•本质:在 JVM 堆内存中开辟一块空间,创建一个纯粹的 Java 空对象,只分配了内存,属性全是默认值。
•源码对应:createBeanInstance()。通常通过无参构造器、带参构造器或工厂方法实现。

2. 属性赋值(Populate)

•本质:完成依赖注入,把 @Autowired、@Resource、XML <property> 等依赖写入字段。
•源码对应:populateBean()。

3. 初始化(Initialization)

•本质:属性赋值完成后,Spring 对 Bean 进行“打磨和包装”,包括 Aware 回调、BeanPostProcessor 前后置、初始化方法,以及 AOP 代理生成。
•源码对应:initializeBean()。

⚠️ 关键校准:属性赋值和初始化都属于创建阶段,但它们是两个独立步骤。populateBean() 在 initializeBean() 之前执行,不是 initializeBean() 内部的一部分。本文重点拆解的是 initializeBean() 的四部曲。


02二、实例化:空壳对象的诞生

Spring 是如何创建这个空对象的?来看 createBeanInstance 的核心逻辑。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {    // 实例化阶段完成    // 判断是否需要提前暴露:单例、允许循环引用、当前正在创建    boolean earlySingletonExposure = mbd.isSingleton()            && this.allowCircularReferences            && isSingletonCurrentlyInCreation(beanName);    if (earlySingletonExposure) {        // 将半成品 Bean 包装成 ObjectFactory,放入三级缓存        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));    }    // 属性赋值    populateBean(beanName, mbd, instanceWrapper);    // 初始化    exposedObject = initializeBean(beanName, exposedObject, mbd);    return exposedObject;}

核心点:在这一步,Spring 只是通过反射或 CGLIB 创建了一个普通 Java 对象。它甚至不知道自己属于哪个 Spring 容器,字段也还没有被注入。


03三、初始化前夜:三级缓存与循环依赖

实例化完成后,紧接着是属性赋值。但在 populateBean 执行之前,Spring 做了一件极其精妙的事情:提前暴露半成品。这是解决循环依赖的核心。

在 doCreateBean 中,有这样一段关键逻辑:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {    // 实例化阶段完成    // 判断是否需要提前暴露:单例、允许循环引用、当前正在创建    boolean earlySingletonExposure = mbd.isSingleton()            && this.allowCircularReferences            && isSingletonCurrentlyInCreation(beanName);    if (earlySingletonExposure) {        // 将半成品 Bean 包装成 ObjectFactory,放入三级缓存        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));    }    // 属性赋值    populateBean(beanName, mbd, instanceWrapper);    // 初始化    exposedObject = initializeBean(beanName, exposedObject, mbd);    return exposedObject;}

Spring 6.0 中三级缓存的数据结构如下:

// 一级缓存:存放已经初始化完成的单例 Beanprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:存放早期的单例 Bean,可能已经是 AOP 代理private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存:存放用于提前暴露的工厂 ObjectFactoryprivate final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

为什么需要 getEarlyBeanReference?

假设 A 依赖 B,B 又依赖 A。

1A 实例化后、属性填充前,被包装成 ObjectFactory 放入三级缓存;
2A 填充属性需要 B,B 开始创建;
3B 填充属性需要 A,从三级缓存拿到 ObjectFactory,调用 getObject(),也就是 getEarlyBeanReference;
4如果 A 需要 AOP 代理,Spring 在这一步提前生成 A 的早期代理对象,放入二级缓存;
5B 注入的就是 A 的早期引用,B 完成初始化;
6A 拿到完整的 B,继续走完属性填充和初始化。

⚠️ 边界校准:只有需要 AOP 代理的 Bean,才会在 getEarlyBeanReference 阶段提前生成早期代理;普通 Bean 的早期引用仍是原始对象。后续 postProcessAfterInitialization 会检测是否已提前生成代理,避免重复创建。

⚠️ 版本提醒:Spring Boot 2.6+ 默认禁止循环依赖,字段注入也可能直接启动失败。本文讲的是传统允许循环依赖场景下的三级缓存机制;生产环境更推荐通过重构消除循环依赖,而不是依赖三级缓存兜底。


04四、初始化四部曲:打磨最终的“完全体”

属性赋值完成后,Spring 进入 initializeBean 阶段。这里发生的就是初始化四部曲。

protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {    // 1. Aware 接口回调:让 Bean 感知 Spring 容器    invokeAwareMethods(beanName, bean);    Object wrappedBean = bean;    if (mbd == null || !mbd.isSynthetic()) {        // 2. 前置处理器:处理 @PostConstruct 等注解        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);    }    try {        // 3. 执行自定义初始化方法:InitializingBean 或 init-method        invokeInitMethods(beanName, wrappedBean, mbd);    } catch (Throwable ex) {        throw new BeanCreationException(beanName, "Invocation of init method failed", ex);    }    if (mbd == null || !mbd.isSynthetic()) {        // 4. 后置处理器:AOP 代理生成的最终战场        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);    }    return wrappedBean;}

Step 1:Aware 接口回调

如果 Bean 实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 等接口,Spring 会通过 invokeAwareMethods 将容器信息注入给 Bean。

⚠️ 严谨细节:ApplicationContextAware不是在这里触发的,它由 ApplicationContextAwareProcessor 这个 BeanPostProcessor 在初始化前置处理阶段注入。

Step 2:前置处理器

Spring 会遍历所有 BeanPostProcessor,执行 postProcessBeforeInitialization。

我们常用的 @PostConstruct 注解,就是由 CommonAnnotationBeanPostProcessor 在这一步负责解析并调用的。

Step 3:执行自定义初始化

•如果类实现了 InitializingBean 接口,会调用其 afterPropertiesSet();
•如果 XML 或 @Bean(initMethod = "init") 配置了初始化方法,也会在此时触发;
•执行顺序:先 afterPropertiesSet(),再自定义 init-method。

Step 4:后置处理器与 AOP 生成(核心)

这是 Spring AOP 魔法发生的地方。

Spring 会在此时触发 AbstractAutoProxyCreator 的 postProcessAfterInitialization。它会判断当前 Bean 是否匹配了切面(Aspect)。如果匹配,Spring 会利用 JDK 动态代理或 CGLIB,在这里将原始 Bean 替换为 AOP 代理对象。

注意:此时方法返回的 wrappedBean 已经不再是原始 Java 对象,而是一个被 Spring 包装过的、带有拦截器链的代理对象。


05五、完整流程总结

通过源码级拆解,Spring Bean 的初始化流程可以总结为:

1实例化:通过构造器或工厂方法创建空对象,属性未赋值;构造器注入在此校验依赖,循环依赖容易 fail-fast。
2提前暴露:将半成品包装成 ObjectFactory 放入三级缓存;必要时(涉及 AOP)提前生成早期代理放入二级缓存。
3属性赋值:通过 populateBean() 完成依赖注入,如 @Autowired。
4初始化:执行 Aware 回调、@PostConstruct、自定义初始化方法,最后在 postProcessAfterInitialization 中完成 AOP 代理包装,最终得到功能完备的单例 Bean。
5使用与销毁:Bean 就绪后处理业务请求;容器关闭时执行已注册的销毁回调。

关键边界:

•属性赋值和初始化同属创建阶段,但 populateBean() 不是 initializeBean() 内部步骤。
•注册销毁回调发生在创建期末尾,执行销毁回调发生在容器关闭时。
•以上流程针对默认单例 Bean;Prototype Bean 不注册销毁回调,@PreDestroy 和 DisposableBean.destroy() 容器不会调用,需要手动管理。
•Spring Boot 2.6+ 默认禁止循环依赖,生产环境优先重构,而不是依赖三级缓存。

06六、写在最后

理解了这些源码,你不仅能在面试中对答如流,在面对项目中复杂的 Spring 循环依赖报错或 AOP 失效问题时,也能快速定位根源。

源码剖析到这里,关于 Spring 的扩展点其实还有个大招没讲:BeanPostProcessor 的自定义实现,以及它在中间件(比如缓存、监控)中的实战用法。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料