专栏文章
专栏文章
Spring Framework 系列
1. Spring Framework 系列 #01:Spring 容器启动流程源码分析 2. Spring Framework 系列 #02:Spring 三级缓存与循环依赖 3. Spring Framework 系列 #03:Spring 事务管理 4. Spring Framework 系列 #04:Spring IoC 与 Bean 生命周期 5. Spring Framework 系列 #05:Spring AOP

Spring Framework 系列 #01:Spring 容器启动流程源码分析

发布于 2026-05-26 09:43 👁 10 次阅读
#源码解析#spring#ioc

Spring Framework 的核心是 AbstractApplicationContext#refresh(),这是 IOC 容器启动的主干。本文以 refresh 的 12 步为骨架,重点剖析每步做什么、为什么这样排序、有哪些扩展点。SpringBoot 的 refreshContext 最终也委托到这里。

缩写速查Spring 术语与缩写速查 · 相关笔记Spring 三级缓存与循环依赖 · SpringBoot 启动流程源码分析


目录

章节 说明
整体架构 ApplicationContext 继承体系 + refresh 全貌
阶段一:容器准备 prepareRefresh + obtainBeanFactory
阶段二:BeanFactory 后处理 BFPP / BDRPP,自动装配解析发生在这里
阶段三:Bean 注册与实例化 BPP 注册 + finishBeanFactoryInitialization
阶段四:容器就绪 onRefresh + 事件广播 + LifecycleProcessor
扩展点汇总 Spring 定义 + SpringBoot 实现对照,含自动装配接入路径
常见问题 循环依赖、Bean 初始化顺序、事件丢失
Spring 与 SpringBoot 的设计边界 为什么 SpringBoot 要重新实现配置加载

整体架构

spring refresh flow

ApplicationContext 继承体系

ApplicationContext
  └── ConfigurableApplicationContext          ← 定义 refresh() 接口
        └── AbstractApplicationContext        ← refresh() 的唯一实现
              ├── AnnotationConfigApplicationContext          (NONE)
              └── AbstractRefreshableApplicationContext
                    └── AbstractRefreshableConfigApplicationContext
                          └── ClassPathXmlApplicationContext  (XML 方式)

SpringBoot 扩展:
AbstractApplicationContext
  └── GenericApplicationContext
        └── AnnotationConfigApplicationContext
              └── AnnotationConfigServletWebServerApplicationContext  (Web)

refresh() 12 步全貌

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // ── 阶段一:容器准备 ──────────────────────────────────
        prepareRefresh();                           // 1. 上下文状态、属性源初始化
        ConfigurableListableBeanFactory beanFactory
            = obtainFreshBeanFactory();             // 2. 获取/刷新 BeanFactory

        // ── 阶段二:BeanFactory 后处理 ────────────────────────
        prepareBeanFactory(beanFactory);            // 3. 配置 BeanFactory 基础设施
        postProcessBeanFactory(beanFactory);        // 4. 🔌 子类扩展点(注册 Web 相关)
        invokeBeanFactoryPostProcessors(beanFactory); // 5. 🔌 执行 BFPP(解析 @Configuration)

        // ── 阶段三:Bean 注册与实例化 ─────────────────────────
        registerBeanPostProcessors(beanFactory);    // 6. 🔌 注册 BPP(AOP/事务代理)
        initMessageSource();                        // 7. 国际化
        initApplicationEventMulticaster();          // 8. 事件广播器
        onRefresh();                                // 9. 🔌 子类扩展(SpringBoot:Tomcat 启动)
        registerListeners();                        // 10. 注册 ApplicationListener
        finishBeanFactoryInitialization(beanFactory); // 11. 实例化所有非懒加载 Bean

        // ── 阶段四:容器就绪 ──────────────────────────────────
        finishRefresh();                            // 12. 发布 ContextRefreshedEvent,启动 Lifecycle
    }
}

为什么这个顺序? 核心约束:BPP(BeanPostProcessor)必须在普通 Bean 之前实例化,否则它无法拦截普通 Bean 的创建。BFPP 必须在 BPP 之前执行,因为它负责解析 BeanDefinition(BPP 本身就是一种 BeanDefinition)。


阶段一:容器准备

1. prepareRefresh()

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    // 🔌 子类可覆写:注册 PropertySource(SpringBoot 在这里注册 servlet 相关属性源)
    initPropertySources();

    // 校验必须存在的属性(通过 setRequiredProperties 指定)
    getEnvironment().validateRequiredProperties();

    // 保存早期事件,等事件广播器就绪后再统一发布
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

关键earlyApplicationEvents 的存在是为了解决"事件广播器在步骤 8 才初始化,但步骤 1~7 可能需要发布事件"的问题。

2. obtainFreshBeanFactory()


阶段二:BeanFactory 后处理

3. prepareBeanFactory()

为 BeanFactory 注册基础设施 Bean,使框架能力生效:

注册内容 作用
ApplicationContextAwareProcessor 回调 *Aware 接口:EnvironmentAwareEmbeddedValueResolverAwareResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAwareApplicationContextAware
ApplicationListenerDetector 检测实现了 ApplicationListener 的 Bean 并自动注册到广播器
Environment / SystemProperties / SystemEnvironment 注册为 singleton,可直接 @Autowired
LoadTimeWeaverAwareProcessor 支持 Load-Time Weaving(AspectJ)
EventListenerMethodProcessor 扫描 @EventListener 注解方法,在步骤⑪ Bean 实例化后转为 ApplicationListener 注册

4. postProcessBeanFactory() 🔌

空实现,子类覆写。SpringBoot Web 在这里注册:

5. invokeBeanFactoryPostProcessors() 🔌

这是整个 refresh 中最复杂的一步,自动装配在这里完成。

执行两类接口(有严格优先级顺序):

BeanDefinitionRegistryPostProcessor(先执行,可新增 BeanDefinition)
  └── ConfigurationClassPostProcessor(优先级最高)
        ├── 解析 @Configuration / @Component / @Bean
        ├── 处理 @Import / @ImportResource
        └── 处理 @EnableAutoConfiguration → 加载 spring.factories 中的 AutoConfiguration

BeanFactoryPostProcessor(后执行,只能修改已有 BeanDefinition)
  └── PropertySourcesPlaceholderConfigurer(解析 @Value 中的 ${...})

执行顺序(保证 BDRPP 在 BFP 之前,内部按 PriorityOrdered > Ordered > 无序)

① PriorityOrdered 的 BDRPP(含 ConfigurationClassPostProcessor)
② Ordered 的 BDRPP
③ 无序的 BDRPP
④ PriorityOrdered 的 BFP
⑤ Ordered 的 BFP
⑥ 无序的 BFP

扩展点:实现 BeanDefinitionRegistryPostProcessor 可在运行时动态注册 BeanDefinition(MyBatis 的 MapperScannerConfigurer 就是这样扫描 Mapper 接口的)。


阶段三:Bean 注册与实例化

6. registerBeanPostProcessors() 🔌

只注册,不执行——将所有 BeanPostProcessor Bean 实例化并有序注册到 BeanFactory。

BeanPostProcessor 触发时机 典型实现
InstantiationAwareBeanPostProcessor Bean 实例化前后 AutowiredAnnotationBeanPostProcessor(处理 @Autowired
BeanPostProcessor#postProcessBeforeInitialization @PostConstruct CommonAnnotationBeanPostProcessor
BeanPostProcessor#postProcessAfterInitialization InitializingBean#afterPropertiesSet AbstractAutoProxyCreator(AOP 代理)
DestructionAwareBeanPostProcessor Bean 销毁前 @PreDestroy 处理

注册顺序(与 BFPP 相同):PriorityOrderedOrdered → 无序 → MergedBeanDefinitionPostProcessor(内部用)

7-8. MessageSource + ApplicationEventMulticaster

// 7. 国际化:查找名为 "messageSource" 的 Bean,没有则用空实现
initMessageSource();

// 8. 事件广播器:查找名为 "applicationEventMulticaster" 的 Bean,没有则用 SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// 此步骤完成后,earlyApplicationEvents 中积压的早期事件被统一发布

自定义异步事件广播:注册名为 applicationEventMulticaster 的 Bean,设置 TaskExecutor,即可让所有事件异步分发。

9. onRefresh() 🔌

子类扩展点,空实现。SpringBoot 最重要的覆写

// ServletWebServerApplicationContext#onRefresh()
@Override
protected void onRefresh() {
    super.onRefresh();
    createWebServer();  // 创建并启动内嵌 Tomcat/Jetty/Undertow/Netty
}

为什么 Tomcat 在 Bean 实例化之前启动? 实际上不是——onRefresh(步骤 9)在 finishBeanFactoryInitialization(步骤 11)之前,但 Tomcat 此时只是"创建并初始化",并不开放端口接受请求。端口开放在步骤 12 的 finishRefresh 里。

10. registerListeners()

将已注册的 ApplicationListener Bean 绑定到广播器,同时发布所有积压的 earlyApplicationEvents

11. finishBeanFactoryInitialization()

实例化所有非懒加载的 singleton Bean,这是启动最耗时的步骤。

Bean 创建的核心流程:

getBean(beanName)
  └── doCreateBean()
        ├── createBeanInstance()          实例化(反射调用构造器)
        ├── populateBean()                依赖注入(@Autowired / @Value)
        │     └── AutowiredAnnotationBeanPostProcessor
        └── initializeBean()
              ├── invokeAwareMethods()    回调 *Aware 接口
              ├── BPP#beforeInitialization  (含 @PostConstruct)
              ├── afterPropertiesSet()    InitializingBean 接口
              ├── init-method             XML 配置的 init-method
              └── BPP#afterInitialization (AOP 代理在此生成)

三级缓存解决循环依赖

缓存 存储内容 作用
singletonObjects(一级) 完整 Bean 正常获取 Bean 的来源
earlySingletonObjects(二级) 早期暴露的 Bean(可能是代理) 循环依赖时临时持有
singletonFactories(三级) ObjectFactory<?> 延迟生成早期引用,支持 AOP 代理

局限:三级缓存只解决 setter/field 注入的循环依赖。构造器注入的循环依赖无法解决(@Lazy 可绕过)。


阶段四:容器就绪

12. finishRefresh()

protected void finishRefresh() {
    clearResourceCaches();

    // 初始化 LifecycleProcessor(查找名为 "lifecycleProcessor" 的 Bean,否则用默认)
    initLifecycleProcessor();

    // 启动所有 Lifecycle Bean(SmartLifecycle#start())
    // SpringBoot Web:Tomcat 在这里真正开放端口
    getLifecycleProcessor().onRefresh();

    // 发布 ContextRefreshedEvent
    publishEvent(new ContextRefreshedEvent(this));
}

Lifecycle 执行顺序SmartLifecyclegetPhase() 从小到大启动(从大到小停止)。Tomcat 的 phase 是 Integer.MAX_VALUE,保证最后启动、最先停止。


扩展点汇总

Spring 定义扩展点接口,SpringBoot 通过 spring.factories 注册实现类,在对应步骤自动触发。第四列标注了 SpringBoot 的具体实现,数据来自 spring-boot-2.4.4.jar。

Spring 扩展点 接口 / 方法 触发步骤 Spring 内置实现 SpringBoot 实现(来自 spring.factories) 通用用途
属性源初始化 initPropertySources() 覆写 步骤① StandardServletEnvironment(注册 servlet 相关属性源) 注册自定义 PropertySource
容器子类扩展 postProcessBeanFactory() 覆写 步骤④ request/session/application scope 注册 Web 相关 Bean 注册
BeanDefinition 注册 BeanDefinitionRegistryPostProcessor 步骤⑤ ConfigurationClassPostProcessor(解析 @Configuration/@ComponentScan/@Import/@Bean
MapperScannerConfigurer(MyBatis Mapper 扫描)
ConfigurationClassPostProcessor 被 Spring Boot 进一步扩展,加载 @EnableAutoConfiguration 动态扫描注册 Bean
BeanDefinition 修改 BeanFactoryPostProcessor 步骤⑤ PropertySourcesPlaceholderConfigurer(解析 ${...}
EventListenerMethodProcessor(处理 @EventListener
修改属性值、替换占位符
BPP 注册 BeanPostProcessor 步骤⑥ AutowiredAnnotationBeanPostProcessor@Autowired/@Value
CommonAnnotationBeanPostProcessor@Resource/@PostConstruct/@PreDestroy
AbstractAutoProxyCreator(AOP 代理)
ApplicationContextAwareProcessor*Aware 接口回调)
ApplicationListenerDetector(自动注册 ApplicationListener Bean)
Bean 初始化前后拦截
Bean 实例化拦截 InstantiationAwareBeanPostProcessor 步骤⑪ AutowiredAnnotationBeanPostProcessor(依赖注入)
AbstractAutoProxyCreator(返回代理)
返回代理替代真实实例
初始化回调 InitializingBean#afterPropertiesSet 步骤⑪ 各框架 Bean 自身实现(如 AbstractMessageConverterMethodArgumentResolver 业务 Bean 自身实现 Bean 自身初始化逻辑
Web 容器启动 onRefresh() 覆写 步骤⑨ ServletWebServerApplicationContext#onRefresh(创建内嵌 Tomcat,未开放端口) 启动 Web 容器
Listener 注册 ApplicationListener 步骤⑩ SourceFilteringListener(代理监听器,过滤事件源) ClearCachesApplicationListener(清缓存)
EnvironmentPostProcessorApplicationListener(加载配置)
LoggingApplicationListener
BackgroundPreinitializer
监听生命周期事件
Context 初始化 ApplicationContextInitializer 步骤③ applyInitializers ConfigurationWarningsApplicationContextInitializer
ContextIdApplicationContextInitializer
SharedMetadataReaderFactoryContextInitializer
refresh 前回调
Lifecycle 控制 SmartLifecycle 步骤⑫ LifecycleProcessor(默认 DefaultLifecycleProcessor,按 phase 排序启停) WebServerStartStopLifecycle(phase=MAX_VALUE,最后开放端口) 控制 Bean 启停顺序
容器就绪 ApplicationListener<ContextRefreshedEvent> 步骤⑫ ClearCachesApplicationListener 容器完全就绪后执行
Bean 销毁 DisposableBean / @PreDestroy context.close() DisposableBeanAdapter(统一包装销毁逻辑) 资源清理

SpringBoot 新增的扩展点(Spring 原生没有)

SpringBoot 在 Spring 之上额外定义了 4 个扩展点,弥补了 Spring 覆盖不到的场景:

SpringBoot 扩展点 触发时机 实现类示例 用途
EnvironmentPostProcessor prepareEnvironment(早于 refresh 步骤⑤) ConfigDataEnvironmentPostProcessorRandomValuePropertySourceEnvironmentPostProcessor 配置文件加载、属性解密
SpringApplicationRunListener SpringBoot 自身生命周期(starting/prepared/started/running) EventPublishingRunListener 监听 SpringBoot 启动事件
ApplicationRunner / CommandLineRunner refresh 完成后,比 SmartLifecycle 更晚 业务自定义 数据预热、缓存初始化
FailureAnalyzer 启动异常时 BeanCurrentlyInCreationFailureAnalyzer 友好诊断启动失败原因

自动装配接入路径(扩展点串联)

① 构造阶段
   spring.factories → ApplicationContextInitializer
   └─ SharedMetadataReaderFactoryContextInitializer 注册

② prepareEnvironment
   spring.factories → EnvironmentPostProcessor(SpringBoot 扩展点)
   └─ ConfigDataEnvironmentPostProcessor → 加载 application.yml

③ prepareContext → load(启动类)
   └─ 注册 @SpringBootApplication 启动类为 BeanDefinition

④ refresh 步骤③ applyInitializers
   └─ SharedMetadataReaderFactoryContextInitializer 回调
      → 注册共享 MetadataReader 的 BDRPP

⑤ refresh 步骤⑤ invokeBeanFactoryPostProcessors(Spring 扩展点)
   └─ ConfigurationClassPostProcessor
      → 解析 @EnableAutoConfiguration
      → AutoConfigurationImportSelector
      → spring.factories EnableAutoConfiguration 列表
      → @Conditional 过滤
      → 注册满足条件的 AutoConfiguration BeanDefinition

常见问题

Q1:为什么 BeanPostProcessor 不能依赖普通 Bean?

BPP 在步骤 6 实例化,普通 Bean 在步骤 11 实例化。如果 BPP 依赖普通 Bean,Spring 会提前实例化那个普通 Bean,但此时其他 BPP 还没注册,导致该普通 Bean 跳过了 AOP 代理等处理。Spring 会打印警告:Bean 'xxx' is not eligible for getting processed by all BeanPostProcessors

解决:BPP 尽量只依赖其他 BPP 或基础设施 Bean,不依赖业务 Bean。

Q2:@EventListener 和 ApplicationListener 的区别?

ApplicationListener @EventListener
注册时机 步骤 10(registerListeners) 步骤 11(Bean 实例化后,由 EventListenerMethodProcessor 注册)
能收到 ContextRefreshedEvent ✅(步骤 12 发布时已注册) ❌(步骤 12 发布时 @EventListener 还未全部注册完)
泛型事件支持 有限 ✅(@EventListener 支持泛型参数)

如果需要监听 ContextRefreshedEvent,必须用 ApplicationListener,不能用 @EventListener

Q3:构造器注入的循环依赖怎么解?

三级缓存不覆盖构造器注入。解决方案:

  1. @Lazy 注解延迟其中一个的注入
  2. 重新审视设计,循环依赖通常意味着职责划分有问题
  3. 提取公共依赖到第三个 Bean

Q4:@PostConstructafterPropertiesSet 哪个先执行?

@PostConstruct 先执行(由 CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization 触发),afterPropertiesSet 后执行(在 initializeBean 中直接调用)。


Spring 与 SpringBoot 的设计边界

理解这个边界,是理解"SpringBoot 为什么要重新实现配置文件加载"的关键。

Spring 的设计哲学:提供机制,不强加策略

Spring Framework 官方文档的原话:

Spring makes it possible to build applications from "plain old Java objects" (POJOs) and to apply enterprise services non-invasively to POJOs.

non-invasive(非侵入性) 是 Spring 的核心设计词。它的含义是:Spring 提供能力,但不规定你该怎么用、配置文件叫什么名字、放在哪里。不同场景(Web 服务、桌面应用、嵌入式系统、批处理)的约定完全不同,Spring 不想为某一类场景做硬编码决策。

因此 Spring 原生的配置文件加载只提供了机制:

// Spring 原生:必须显式声明,没有约定
@PropertySource("classpath:application.properties")
@PropertySource("classpath:database.properties")
public class AppConfig { }

Environment 默认只有 systemPropertiessystemEnvironment 两个属性源,其他全靠显式注册。

SpringBoot 的设计哲学:固执己见的默认值

SpringBoot 官方文档的原话:

Spring Boot takes an opinionated view of building production-ready Spring applications.

opinionated(固执己见) 是 SpringBoot 主动选择的定位。它针对 "生产级 Web 服务"这一具体场景,替你做好了所有决策:

决策项 SpringBoot 的约定
配置文件叫什么 application.yml / application.properties
放在哪里 classpath:/classpath:/config/file:./ 等 5 个位置
多环境怎么切换 application-{profile}.yml
优先级顺序 file:./ 系列 > classpath:/ 系列
支持什么格式 .properties / .yml,可通过 PropertySourceLoader 扩展

这就是为什么 SpringBoot 要重新实现配置文件加载——不是 Spring 没有这个能力,而是 Spring 故意不做这层约定,SpringBoot 专门为 Web 场景补上了这一层。

两者的边界

Spring Framework
  └── 提供机制:PropertySource、Environment、ApplicationContextInitializer、
               BeanPostProcessor、ApplicationListener……
      (只问"怎么做",不问"做什么")

SpringBoot
  └── 定义策略:application.yml 自动发现、profile 切换、
               AutoConfiguration 条件装配、内嵌 Tomcat……
      (基于 Spring 的机制,回答"该做什么")

说明:上述"机制与策略分离"的表述是对两者关系的归纳,非官方术语。官方原词是 Spring 的 "non-invasive" 和 SpringBoot 的 "opinionated",两者合在一起能准确传达这层含义。


参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。

发表评论