Spring 的启动机制:refresh() 执行序列与 @Configuration 类处理详解
前置知识
- ›第 2 篇:BeanFactory 层次结构与 BeanPostProcessor
- ›熟悉 Java 注解与反射机制
Spring 的启动机制:refresh() 执行序列与 @Configuration 类处理详解
无论是命令行工具、Web 服务器还是微服务,每个 Spring 应用的启动都始于同一个方法调用:AbstractApplicationContext.refresh()。这个方法统筹协调了整个启动流程——准备环境、扫描组件、处理 @Configuration 类、注册后处理器、实例化所有单例 Bean。理解 refresh() 的运作方式,是理解第 2 篇中 IoC 容器如何被填充和激活的关键。
ApplicationContext:不只是 BeanFactory
在深入 refresh() 之前,有必要先了解 ApplicationContext 在上一篇介绍的 BeanFactory 基础上究竟扩展了哪些能力。接口声明已经说明了一切:
spring-context/src/main/java/org/springframework/context/ApplicationContext.java#L59-L60
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher,
ResourcePatternResolver {
classDiagram
class ApplicationContext {
<<interface>>
}
class ListableBeanFactory {
<<interface>>
Bean enumeration
}
class HierarchicalBeanFactory {
<<interface>>
Parent-child contexts
}
class EnvironmentCapable {
<<interface>>
Profiles + properties
}
class MessageSource {
<<interface>>
i18n message resolution
}
class ApplicationEventPublisher {
<<interface>>
Event broadcasting
}
class ResourcePatternResolver {
<<interface>>
Classpath scanning
}
ListableBeanFactory <|-- ApplicationContext
HierarchicalBeanFactory <|-- ApplicationContext
EnvironmentCapable <|-- ApplicationContext
MessageSource <|-- ApplicationContext
ApplicationEventPublisher <|-- ApplicationContext
ResourcePatternResolver <|-- ApplicationContext
ApplicationContext 通过 ListableBeanFactory 和 HierarchicalBeanFactory 组合了 BeanFactory 的能力,并在此基础上增加了四项核心功能:环境抽象(Profile 与属性源)、国际化支持、事件发布机制以及 classpath 资源扫描。这些并非锦上添花,而是配置处理流水线所依赖的基础设施。
两个最常用的具体实现类分别是:
AnnotationConfigApplicationContext— 用于独立的基于注解的应用,内部会创建DefaultListableBeanFactory并注册AnnotatedBeanDefinitionReader。GenericWebApplicationContext— 用于 Web 应用,通常由 Spring Boot 的SpringApplication负责创建。
refresh() 的 13 个步骤
refresh() 是 Spring Framework 中最重要的方法。让我们完整地看一遍它的执行过程:
sequenceDiagram
participant App as Application
participant AAC as AbstractApplicationContext
participant BF as BeanFactory
participant BPPs as BeanPostProcessors
App->>AAC: refresh()
Note over AAC: 1. prepareRefresh()
Note right of AAC: Set startup time, active flag,<br/>validate required properties
AAC->>BF: 2. obtainFreshBeanFactory()
Note right of BF: Get or create the<br/>DefaultListableBeanFactory
AAC->>BF: 3. prepareBeanFactory(beanFactory)
Note right of BF: Register environment beans,<br/>system properties, ClassLoader
Note over AAC: 4. postProcessBeanFactory()
Note right of AAC: Template method for subclasses<br/>(web contexts register scopes)
AAC->>BF: 5. invokeBeanFactoryPostProcessors()
Note right of BF: 🔥 ConfigurationClassPostProcessor<br/>runs here — discovers all beans
AAC->>BF: 6. registerBeanPostProcessors()
Note right of BF: Sort & register all BPPs<br/>by PriorityOrdered/Ordered
Note over AAC: 7. initMessageSource()
Note over AAC: 8. initApplicationEventMulticaster()
Note over AAC: 9. onRefresh()
Note right of AAC: Template method<br/>(web contexts start server)
Note over AAC: 10. registerListeners()
AAC->>BF: 11. finishBeanFactoryInitialization()
Note right of BF: Pre-instantiate ALL<br/>non-lazy singletons
Note over AAC: 12. finishRefresh()
Note right of AAC: Publish ContextRefreshedEvent,<br/>start Lifecycle beans
Note over AAC: 13. Exception handling
Note right of AAC: destroyBeans() + cancelRefresh()<br/>on any failure
以下三个步骤是整个流程中最关键的环节:
第 5 步 — invokeBeanFactoryPostProcessors() 是整个启动过程的核心所在。这一步会调用所有 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 实例,其中最重要的是 ConfigurationClassPostProcessor——它负责扫描 classpath、处理 @Configuration 类、解析 @ComponentScan、处理 @Import 调用链,并注册所有发现的 BeanDefinition。在第 5 步执行之前,容器中只有少量基础设施 Bean;执行之后,整个应用的 BeanDefinition 才全部就绪。
第 6 步 — registerBeanPostProcessors() 会找出在第 5 步中生成的所有 BeanPostProcessor BeanDefinition,并按优先级顺序将其实例化。正如第 2 篇所介绍的,BeanPostProcessor 是 Spring 的通用扩展钩子,这一步相当于为后续所有操作完成了"武装准备"。
第 11 步 — finishBeanFactoryInitialization() 会触发 DefaultListableBeanFactory.preInstantiateSingletons(),遍历所有已注册的 BeanDefinition,并对每个非懒加载单例调用 getBean()。这是第 2 篇中介绍的 Bean 创建流水线真正为业务 Bean 执行的时刻。
提示: 如果你的应用启动缓慢,瓶颈几乎都出在第 5 步(classpath 扫描)或第 11 步(单例实例化)。Spring Boot 的启动 Actuator 可以按步骤追踪耗时,建议关注
spring.context.beans.post-process和spring.context.refresh这两个启动指标。
ConfigurationClassPostProcessor:驱动基于注解的配置处理
ConfigurationClassPostProcessor 可以说是 Spring 中最复杂的类。它超过 1100 行,同时实现了 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor,因此在第 5 步的两个不同阶段都拥有介入点:
从它的 import 列表(第 44–75 行)可以窥见其职责之广——涵盖 AOT 代码生成、Bean 注册、注解处理,乃至属性源处理:
具体的处理流程委托给 ConfigurationClassParser 来完成:
flowchart TD
CCPP["ConfigurationClassPostProcessor<br/>postProcessBeanDefinitionRegistry()"]
CCPP --> FindCandidates["Find @Configuration candidates<br/>in existing bean definitions"]
FindCandidates --> CCP["ConfigurationClassParser.parse()"]
CCP --> ProcessConfig["Process @Configuration class"]
ProcessConfig --> CS["@ComponentScan<br/>→ ClassPathBeanDefinitionScanner"]
ProcessConfig --> Import["@Import<br/>→ Recursive processing"]
ProcessConfig --> Bean["@Bean methods<br/>→ BeanDefinition registration"]
ProcessConfig --> PS["@PropertySource<br/>→ PropertySource registration"]
ProcessConfig --> IC["@ImportResource<br/>→ XML import"]
ProcessConfig --> BR["BeanRegistrar<br/>→ Programmatic registration"]
Import --> IS["ImportSelector<br/>(Spring Boot auto-config)"]
Import --> IBR["ImportBeanDefinitionRegistrar"]
Import --> RecurseConfig["Another @Configuration<br/>→ Recurse"]
CS --> NewConfigs["Discovered @Configuration classes"]
NewConfigs --> CCP
这个解析过程是递归的。当遇到 @ComponentScan 时,会触发 classpath 扫描器,发现新的 @Configuration 类并继续处理;当遇到 @Import 时,则沿着导入链递归展开。正是这一机制,使得一个 @SpringBootApplication 注解能够引入数百个自动配置类。
在处理某个配置类之前,ConfigurationClassParser 会先对 @Conditional 注解进行条件评估。这正是 Spring Boot 条件化自动配置的底层机制——@ConditionalOnClass、@ConditionalOnMissingBean 等条件注解均依赖于此。
延迟导入处理:Spring Boot 自动配置的入口
解析器以两种方式处理 ImportSelector 的实现:
- 普通
ImportSelector— 在解析阶段立即处理。 DeferredImportSelector— 收集起来,等所有其他配置类处理完毕后再统一处理。
Spring Boot 的 AutoConfigurationImportSelector 实现了 DeferredImportSelector。这样可以确保用户自定义的 Bean 优先完成注册,从而让 @ConditionalOnMissingBean 能够正确检测到用户是否已经提供了自定义实现。
这个细微的顺序保证,正是 Spring Boot "开箱即用、随时可覆盖"模式得以实现的根本。如果没有延迟处理机制,自动配置与用户配置之间就会产生竞争,导致不可预期的结果。
BeanRegistrar:面向 AOT 时代的编程式 Bean 注册
Spring 7.x 引入了一套全新的编程式 Bean 注册 API:
spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistrar.java#L22-L80
class MyBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class);
registry.registerBean("bar", Bar.class, spec -> spec
.prototype()
.lazyInit()
.supplier(context -> new Bar(context.bean(Foo.class))));
}
}
为什么要增加这套注册机制?答案是 AOT(提前编译),我们将在第 6 篇中详细探讨。传统的 @Bean 方法依赖运行时反射——框架必须通过反射调用方法来创建 Bean。而 BeanRegistrar 通过 supplier() lambda 提供了显式的代码级 Bean 创建方式,可在构建时进行分析和优化。
flowchart LR
subgraph "@Configuration + @Bean"
A1["Runtime reflection"] --> A2["Invoke method reflectively"]
A2 --> A3["Return bean instance"]
end
subgraph "BeanRegistrar"
B1["Explicit supplier lambda"] --> B2["Direct constructor call"]
B2 --> B3["Return bean instance"]
end
A1 -.->|"AOT must generate<br/>reflection hints"| AOT1["AOT overhead"]
B1 -.->|"Already AOT-friendly<br/>no reflection needed"| AOT2["Zero AOT overhead"]
BeanRegistrar 通过 @Import 与 ConfigurationClassParser 集成:
@Configuration
@Import(MyBeanRegistrar.class)
class MyConfiguration { }
需要特别注意的是,BeanRegistrar 的实现类不是 Spring 组件——它们使用无参构造函数,也不支持依赖注入。这个限制是有意为之:这样一来,AOT 处理阶段可以直接对其进行实例化,无需任何额外开销。
提示:
BeanRegistrar的 Javadoc 明确说明:如果在实现类上添加@Component注解,或通过@Bean方法返回其实例,只会将其作为普通 Bean 注册,不会触发register()方法的调用。只有通过@Import引入,才能真正触发注册逻辑。
线程安全的关闭保障
refresh() 在执行时会先获取 startupShutdownLock(第 583 行),并将整个启动流程包裹在 try-finally 块中。一旦任何步骤抛出 RuntimeException 或 Error,第 628 行的 catch 块会立即执行清理操作:
- 停止所有已启动的
LifecycleBean - 调用
destroyBeans(),销毁在故障发生前已创建的所有单例 - 调用
cancelRefresh(),将容器的 active 标志重置
这一机制确保启动失败时不会留下任何悬空资源——无论是部分创建的数据库连接、线程池还是网络监听器,都会被正确关闭。
下一篇预告
至此,我们已经完整追踪了从 refresh() 到 ConfigurationClassPostProcessor 再到 BeanRegistrar 的整个启动序列。下一篇文章将聚焦于这些 Bean 需要横切行为时发生了什么——Spring AOP 代理基础设施如何在第 2 篇中介绍的 postProcessAfterInitialization 阶段,透明地为 Bean 织入 @Transactional、@Async 等拦截器。