前言

Spring 框架是 Java 开发者的基石,而 AbstractApplicationContext.refresh() 方法则是这块基石的心脏。无论是 Spring MVC、Spring Boot 还是普通的 Spring 应用,容器的启动过程最终都会调用到这个方法。

很多人觉得 Spring 源码晦涩难懂,实际上只要抓住 “一条主线(refresh)”“几个关键扩展点(PostProcessor)”,就能化繁为简。

本文将通过图解的方式,深入剖析 refresh() 的全流程,并重点拆解 Bean 的生命周期三级缓存解决循环依赖以及 AOP 代理的生成时机


一、 refresh():容器启动的总指挥

refresh() 方法定义在 AbstractApplicationContext 中,它定义了整个容器初始化的标准模板。无论你使用的是 XML 配置还是注解配置,流程都是一样的。

1.1 核心流程图

容器启动 refresh

1. prepareRefresh
准备工作,记录时间,环境检查
2. obtainFreshBeanFactory
创建BeanFactory, 加载BeanDefinition
3. prepareBeanFactory
配置工厂标准组件,类加载器等
4. postProcessBeanFactory
子类扩展钩子
5. invokeBeanFactoryPostProcessors
执行BeanFactory后置处理器
解析配置类,扫描包
6. registerBeanPostProcessors
注册Bean后置处理器
拦截Bean创建过程
7. initMessageSource
初始化国际化组件
8. initApplicationEventMulticaster
初始化事件广播器
9. onRefresh
子类扩展,如SpringBoot启动Tomcat
10. registerListeners
注册事件监听器
11. finishBeanFactoryInitialization
实例化所有非懒加载单例Bean
(核心重头戏)
12. finishRefresh
发布刷新完成事件

容器启动完成

1.2 源码概览

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备工作
        prepareRefresh();
        // 2. 获取 BeanFactory (加载 BeanDefinition)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 3. 准备 BeanFactory (设置 ClassLoader, Aware 接口支持等)
        prepareBeanFactory(beanFactory);

        try {
            // 4. 子类扩展点
            postProcessBeanFactory(beanFactory);
            // 5. 调用 BeanFactoryPostProcessor (解析 @Configuration, @ComponentScan)
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6. 注册 BeanPostProcessor (AOP 的基石)
            registerBeanPostProcessors(beanFactory);
            // 7. 初始化国际化
            initMessageSource();
            // 8. 初始化事件广播器
            initApplicationEventMulticaster();
            // 9. 子类扩展点 (Spring Boot 在这里启动 Tomcat)
            onRefresh();
            // 10. 注册监听器
            registerListeners();
            // 11. 【核心】实例化所有非懒加载单例 Bean
            finishBeanFactoryInitialization(beanFactory);
            // 12. 完成刷新,发布事件
            finishRefresh();
        } catch (BeansException ex) {
            // 异常处理...
        }
    }
}


二、 核心重头戏:Bean 的生命周期

在第 11 步 finishBeanFactoryInitialization 中,Spring 会遍历所有的 BeanDefinition,通过 getBean() -> doCreateBean() 来创建对象。

这不是简单的 new,而是一个复杂的流水线工程。

2.1 Bean 创建流水线

Bean 创建生命周期

初始化阶段细节

invokeAwareMethods
设置BeanName, BeanFactory等

BPP.postProcessBeforeInitialization
初始化前置处理

invokeInitMethods
执行自定义init方法

BPP.postProcessAfterInitialization
初始化后置处理 / AOP代理生成

Start: getBean

单例缓存中有吗?

直接返回

doCreateBean

1. createBeanInstance
实例化/new
2. MergedBeanDefinition
处理@Autowired注解元数据
3. 是否允许提前暴露?
(解决循环依赖)

addSingletonFactory
放入三级缓存

4. populateBean
属性填充/依赖注入
5. initializeBean
初始化阶段

放入一级缓存
singletonObjects

End: 返回Bean

关键点解析:

  1. Instantiation (实例化): 仅仅是在堆内存中申请空间,对象属性都是默认值(null)。
  2. Populate (属性填充): 也就是依赖注入(DI),递归调用 getBean 获取依赖对象。
  3. Initialization (初始化): 执行用户自定义的初始化逻辑,以及 AOP 代理的生成(通常在 postProcessAfterInitialization)。

三、 难点攻克:三级缓存与循环依赖

假设 A 依赖 BB 依赖 A。Spring 是如何解决死锁的?

答案是:三级缓存

  • 一级缓存 (singletonObjects): 存放完整的、初始化好的 Bean。
  • 二级缓存 (earlySingletonObjects): 存放半成品 Bean(已实例化,未属性填充)。
  • 三级缓存 (singletonFactories): 存放 ObjectFactory(Lambda 表达式),用于生成 Bean 的早期引用(可能是代理对象)。

3.1 循环依赖解决流程

Bean B Bean A 三级缓存 二级缓存 一级缓存 容器主线程 Bean B Bean A 三级缓存 二级缓存 一级缓存 容器主线程 创建 A (doCreateBean) 3. 填充 A 的属性 (注入 B) 创建 B (因为 B 不在缓存) 填充 B 的属性 (注入 A) 4. A 拿到 B,属性填充完毕 1. 实例化 A (new A) 2. 将 A 的工厂放入三级缓存 getBean(B) 实例化 B (new B) 将 B 的工厂放入三级缓存 getBean(A) 检查一级? (无) 检查二级? (无) 检查三级? (有!) 调用 getEarlyBeanReference() 生成 A 的早期引用 (AOP介入点) 将 A 放入二级缓存 删除三级缓存中的 A B 拿到 A 的引用 (完成注入) B 初始化完成 B 放入一级缓存 返回 B 实例 5. A 初始化完成 A 放入一级缓存

3.2 为什么需要三级缓存?

如果不考虑 AOP,二级缓存就够了。但如果 A 被 AOP 代理了,B 注入的必须是 A 的代理对象,而不是原始对象。

三级缓存中的工厂 getEarlyBeanReference 保证了:只有在发生循环依赖时,才提前通过 AOP 创建代理对象;否则,AOP 代理依然推迟到初始化最后一步进行。


四、 AOP 代理的秘密

AOP 代理对象到底是在什么时候创建的?很多人会误以为只有一种情况,其实分两种:

  1. 正常情况
  • Bean 初始化后 (postProcessAfterInitialization) 创建。
  • 源码类:AbstractAutoProxyCreator
  1. 循环依赖情况
  • Bean 实例化后、属性填充前 (通过三级缓存的 getEarlyBeanReference) 提前创建。
  • 为了防止重复代理,Spring 使用 earlyProxyReferences 集合记录了已经提前代理的 Bean。

五、 总结

看懂 Spring 源码,本质上就是看懂 对象的管理艺术

  1. refresh() 是骨架,定义了容器启动的标准流程。
  2. BeanDefinition 是图纸,描述了 Bean 应该长什么样。
  3. BeanPostProcessor 是工匠,在 Bean 创建的各个阶段进行精修(AOP、注解处理)。
  4. 三级缓存 是应急通道,巧妙解决了循环引用的死结。

希望这篇图解能帮你打破对 Spring 源码的恐惧!


面试题自测:

  1. BeanFactoryFactoryBean 有什么区别?
  2. Spring 为什么不能解决构造函数参数的循环依赖?
  3. @Autowired@Resource 在源码层面有什么区别?

(欢迎在评论区留言讨论!)

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐