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 要重新实现配置加载 |
整体架构
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()
- XML 方式(
AbstractRefreshableApplicationContext):每次 refresh 销毁旧 BeanFactory,重新解析 XML,创建新的DefaultListableBeanFactory,加载所有 BeanDefinition - 注解方式(
GenericApplicationContext):BeanFactory 在构造时已创建,此步骤直接返回,BeanDefinition 由后续 BFPP 解析
阶段二:BeanFactory 后处理
3. prepareBeanFactory()
为 BeanFactory 注册基础设施 Bean,使框架能力生效:
| 注册内容 | 作用 |
|---|---|
ApplicationContextAwareProcessor |
回调 *Aware 接口:EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware |
ApplicationListenerDetector |
检测实现了 ApplicationListener 的 Bean 并自动注册到广播器 |
Environment / SystemProperties / SystemEnvironment |
注册为 singleton,可直接 @Autowired |
LoadTimeWeaverAwareProcessor |
支持 Load-Time Weaving(AspectJ) |
EventListenerMethodProcessor |
扫描 @EventListener 注解方法,在步骤⑪ Bean 实例化后转为 ApplicationListener 注册 |
4. postProcessBeanFactory() 🔌
空实现,子类覆写。SpringBoot Web 在这里注册:
WebApplicationContextServletContextAwareProcessor(感知 ServletContext)request/session/applicationscope
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 相同):PriorityOrdered → Ordered → 无序 → 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 执行顺序:SmartLifecycle 按 getPhase() 从小到大启动(从大到小停止)。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(加载配置)LoggingApplicationListenerBackgroundPreinitializer |
监听生命周期事件 |
| Context 初始化 | ApplicationContextInitializer |
步骤③ applyInitializers | — | ConfigurationWarningsApplicationContextInitializerContextIdApplicationContextInitializerSharedMetadataReaderFactoryContextInitializer |
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 步骤⑤) |
ConfigDataEnvironmentPostProcessor、RandomValuePropertySourceEnvironmentPostProcessor |
配置文件加载、属性解密 |
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:构造器注入的循环依赖怎么解?
三级缓存不覆盖构造器注入。解决方案:
@Lazy注解延迟其中一个的注入- 重新审视设计,循环依赖通常意味着职责划分有问题
- 提取公共依赖到第三个 Bean
Q4:@PostConstruct 和 afterPropertiesSet 哪个先执行?
@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 默认只有 systemProperties 和 systemEnvironment 两个属性源,其他全靠显式注册。
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)
发表评论