Spring 的 refresh() 方法是理解整个 IoC 容器核心逻辑的最佳入口。这个方法就像容器的“启动总纲”,定义了从加载配置到创建所有单例 Bean 的完整流程。下面我帮你把它拆解成几个易于理解的阶段,并告诉你每一步该看什么、怎么调试。

📍 一、先找到 refresh() 方法

  • 所在类org.springframework.context.support.AbstractApplicationContext

  • 版本:以你正在阅读的 5.3.x 为例

  • 定位技巧:在 IDEA 中双击 Shift,输入 AbstractApplicationContext.refresh 直接跳转

这个方法是 public void refresh() throws BeansException, IllegalStateException,它的代码结构非常清晰,由一系列具有明确职责的子方法组成。


🔍 二、宏观俯瞰:refresh() 的整体骨架

先看一眼方法的整体结构,大概有十几个步骤,但核心可以归纳为三大阶段

  1. 准备阶段:为容器启动做准备(环境、监听器等)

  2. BeanFactory 创建与配置阶段:加载配置、注册 BeanPostProcessor、执行 BeanFactoryPostProcessor

  3. 单例 Bean 实例化阶段:初始化所有剩余的单例 Bean(非懒加载)

对应的代码骨架(简化后):

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备上下文
        prepareRefresh();

        // 2. 获取新的 BeanFactory(通常是从新加载的配置中创建)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 3. 对 BeanFactory 进行预填充(如设置类加载器、添加后置处理器等)
        prepareBeanFactory(beanFactory);

        try {
            // 4. 允许子类对 BeanFactory 进行后置处理(模板方法)
            postProcessBeanFactory(beanFactory);

            // 5. 调用 BeanFactoryPostProcessor(重要!)
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 注册 BeanPostProcessor(重要!)
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化消息源(国际化)
            initMessageSource();

            // 8. 初始化事件广播器
            initApplicationEventMulticaster();

            // 9. 模板方法:留给子类刷新特定 bean
            onRefresh();

            // 10. 注册监听器
            registerListeners();

            // 11. **核心:实例化所有非懒加载的单例 Bean**
            finishBeanFactoryInitialization(beanFactory);

            // 12. 最后一步:发布容器刷新完成事件
            finishRefresh();
        } catch (BeansException ex) {
            // 异常处理:销毁已创建的 bean
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        } finally {
            // 重置公共缓存
            resetCommonCaches();
        }
    }
}

🧭 三、关键步骤拆解(建议打断点阅读)

你可以从第 1 步开始逐行 Debug   ,或者直接跳到最核心的 finishBeanFactoryInitialization。但为了更好地理解上下文,建议按顺序跟踪。

1️⃣ prepareRefresh()

  • 做什么:设置启动时间、激活状态、初始化属性源、验证必要属性。

  • 关注点initPropertySources() 是一个模板方法,留给子类扩展(例如 Web 环境会设置 servlet 相关属性)。

  • 断点:可以放在方法内部,观察 environment 的变化。

2️⃣ obtainFreshBeanFactory()

  • 做什么:这是创建或刷新 BeanFactory 的地方。

  • 调用链refreshBeanFactory() → 如果是 AbstractRefreshableApplicationContext(如 XML 或注解配置),会销毁原有的 BeanFactory,重新加载配置,创建新的 DefaultListableBeanFactory,并读取所有 Bean 定义(loadBeanDefinitions)。

  • 关注点:从这里开始,容器拥有了所有 Bean 的元信息(BeanDefinition)。

  • 断点:在 loadBeanDefinitions 方法中,可以观察 XML 或注解是如何被解析成 BeanDefinition 的。

3️⃣ prepareBeanFactory(beanFactory)

  • 做什么:为 BeanFactory 配置标准特性,如:

    • 设置类加载器

    • 添加 ApplicationContextAwareProcessor(处理 Aware 接口)

    • 注册一些特殊的单例 Bean(如 environmentsystemProperties 等)

  • 关注点:这里注册的 BeanPostProcessor 会在后续对所有 Bean 生效。

4️⃣ postProcessBeanFactory(beanFactory)

  • 做什么:空方法,留给子类覆盖(例如在 Web 环境中注册 Web 相关的 Scope 或 ServletContextAwareProcessor)。

  • 断点:如果你在 Web 环境下调试,可以关注子类的实现。

5️⃣ invokeBeanFactoryPostProcessors(beanFactory) ⭐️

  • 做什么:调用所有注册的 BeanFactoryPostProcessor(包括 BeanDefinitionRegistryPostProcessor)。

  • 谁在这里执行:例如 ConfigurationClassPostProcessor(处理 @Configuration 类)、PropertySourcesPlaceholderConfigurer(解析占位符 ${...})。

  • 效果:此时可以对尚未实例化的 BeanDefinition 进行修改或注册新 Bean。

  • 断点:观察 BeanFactoryPostProcessor 的执行顺序,以及 BeanDefinition 的变化。

6️⃣ registerBeanPostProcessors(beanFactory) ⭐️

  • 做什么:将所有实现了 BeanPostProcessor 接口的 Bean 注册到 BeanFactory 的 beanPostProcessors 列表中。注意:这里只是注册,不会调用它们的方法。

  • 顺序:先注册实现了 PriorityOrdered 的,再是 Ordered,最后是普通的。

  • 断点:可以查看 beanPostProcessors 列表的内容。

7️⃣ initMessageSource()

  • 做什么:初始化国际化消息源。

  • 关注点:如果容器中没有名为 messageSource 的 Bean,会注册一个 DelegatingMessageSource 作为空实现。

8️⃣ initApplicationEventMulticaster()

  • 做什么:初始化事件广播器。默认是 SimpleApplicationEventMulticaster

  • 关注点:这里决定了事件发布的方式(同步/异步)。

9️⃣ onRefresh()

  • 做什么:模板方法,留给子类初始化特定的 Bean(例如 Web 容器中的 ThemeSource 或内嵌 Web 服务器)。

  • 断点:如果你在 Spring Boot 中调试,这里会触发 ServletWebServerApplicationContext 的 onRefresh(),从而启动内嵌 Tomcat。

🔟 registerListeners()

  • 做什么:将容器中所有实现了 ApplicationListener 的 Bean 注册到事件广播器,并可能发布早期事件。

  • 关注点:可以观察监听器是如何被添加的。

1️⃣1️⃣ finishBeanFactoryInitialization(beanFactory) ⭐️⭐️⭐️

  • 做什么核心中的核心——实例化所有剩余的非懒加载单例 Bean。

  • 如何实现:调用 beanFactory.preInstantiateSingletons(),该方法会遍历所有 BeanDefinition,依次调用 getBean()(最终触发 doCreateBean)。

  • 关键子步骤

    • 处理 FactoryBean(加 & 前缀获取 FactoryBean 本身)

    • 解决循环依赖(三级缓存登场)

    • 调用 BeanPostProcessor 的 postProcessBeforeInitialization 和 postProcessAfterInitialization

    • 执行 InitializingBean.afterPropertiesSet() 和自定义 init-method

  • 断点:这是最重要的断点位置。可以从 getBean 开始,一路跟踪到 doCreateBean,观察 Bean 的完整生命周期。

1️⃣2️⃣ finishRefresh()

  • 做什么:清理资源缓存、初始化生命周期处理器、发布 ContextRefreshedEvent 事件。

  • 关注点:这里触发了实现了 SmartLifecycle 的 Bean 的启动。


🧪 四、推荐调试路线

  1. 起点:在你的测试类中调用 new AnnotationConfigApplicationContext(MyConfig.class) 后,立即在 refresh() 方法第一行打上断点。

  2. 逐步前进:用 F8(Step Over)逐个步骤执行,观察每个方法调用前后的变化。

  3. 深入核心:当执行到 finishBeanFactoryInitialization 时,用 F7(Step Into)进入该方法,再进入 preInstantiateSingletons(),然后观察 getBean 的调用。

  4. 观察循环依赖:如果你的测试中有循环依赖的场景,可以重点关注三级缓存(singletonObjectsearlySingletonObjectssingletonFactories)的变化。

  5. 观察后置处理器:在 doCreateBean 中,留意 applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization 的调用。


🧠 五、需要理解的设计思想

  • 模板方法模式refresh() 本身就是一个模板方法,定义了大流程,一些步骤(如 postProcessBeanFactoryonRefresh)留给子类实现,以适应不同环境。

  • 工厂方法模式obtainFreshBeanFactory() 通过 refreshBeanFactory() 创建具体的 BeanFactory 实现。

  • 观察者模式:通过事件发布/监听机制,实现容器内各组件之间的解耦。

  • 策略模式BeanFactoryPostProcessor 和 BeanPostProcessor 允许用户插入自定义逻辑,改变 Bean 的定义或实例化过程。

📚 六、阅读过程中可能遇到的难点

难点 解释
循环依赖解决 三级缓存 singletonFactories 是关键,理解“提前暴露”的含义。建议结合 debug 看缓存变化。
BeanPostProcessor 的执行时机 区分两类:在 registerBeanPostProcessors 中注册,在 Bean 实例化前后执行。
FactoryBean 和普通 Bean 注意 & 前缀,理解 getObject() 的调用时机。
AOP 代理的创建 通常由 AnnotationAwareAspectJAutoProxyCreator 在 postProcessAfterInitialization 中创建代理。
事件机制 事件广播器的初始化、监听器的注册、事件的发布流程。

💡 七、最后的小建议

  • 不要试图一次性读懂所有细节:先抓住主线(Bean 的创建),再分支探索(如 AOP、事务、Web 启动)。

  • 结合 Spring 官方文档:文档中对 IoC 容器的解释可以帮助你理解设计意图。

  • 多画图:画出流程时序图或状态图,有助于形成整体认知。

按照这个拆解方法,相信你能够逐步攻破 refresh() 方法,从而掌握 Spring 容器的核心精髓。

Logo

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

更多推荐