Spring Boot Startup
Spring Boot 的"约定优于配置"由三条主线支撑:SpringApplication 对启动过程的编排、IoC 容器的 Bean 加载、基于条件注解的自动配置。本文以 Spring Boot 4.x(底层为 Spring Framework 7)源码为基准,沿启动顺序把这三条主线讲清楚。4.x 最大的工程变化是模块化拆分,启动主线本身保持不变。Bean 的实际使用规范见Spring Boot Bean 使用规范。
启动原理
@SpringBootApplication 拆解
1 | |
@SpringBootApplication 是复合注解,三个组成部分各司其职:
1 | |
@SpringBootConfiguration:标记主类为配置类@EnableAutoConfiguration:自动配置的入口@ComponentScan:扫描主类所在包及子包,两个 excludeFilter 防止自动配置类被当作普通组件重复注册
构造阶段:new SpringApplication()
SpringApplication.run() 分两步:先构造,再运行。构造阶段只做三件事:
- 推断应用类型(
WebApplicationType.deduce(),3.x 中叫deduceFromClasspath()):模块化之后由 spring-boot-webmvc、spring-boot-webflux 各自注册的Deducer策略判定——存在 MVC 为 Servlet,仅有 WebFlux 为 Reactive,皆无为普通应用 - 从
META-INF/spring.factories加载ApplicationContextInitializer与ApplicationListener - 从调用栈推断主类(
deduceMainApplicationClass())
4.x 的构造逻辑与 3.x 一致,但模块边界重划:原 spring-boot 单体拆分为 spring-boot-webmvc、spring-boot-jdbc 等按技术划分的模块,自动配置类随迁;Web starter 相应更名为 spring-boot-starter-webmvc 与 spring-boot-starter-webflux。运行期的一个默认值变化是虚拟线程默认开启(spring.threads.virtual.enabled=true)。
运行阶段:run()
事件是理解启动过程的钥匙:
| 事件 | 发布时机 |
|---|---|
| ApplicationStartingEvent | run 刚开始,环境尚未准备 |
| ApplicationEnvironmentPreparedEvent | Environment 创建后,配置文件在此时加载 |
| ApplicationContextInitializedEvent | 上下文创建完毕、初始化器执行后 |
| ApplicationPreparedEvent | 上下文准备完成,刷新之前 |
| ContextRefreshedEvent | refresh() 结束 |
| ApplicationStartedEvent | 刷新完成,Runner 执行之前 |
| ApplicationReadyEvent | Runner 执行完毕,应用对外可用 |
事件发布由 SpringApplicationRunListeners 完成:它从 spring.factories 加载 EventPublishingRunListener,把生命周期回调转换为 Spring 事件广播出去。
refresh():一切的核心
refreshContext() 最终调用 AbstractApplicationContext.refresh(),Spring 最经典的模板方法:
1 | |
12 个步骤中,两步是理解 Bean 加载的关键:
- 步骤 5:解析所有配置类,把
@Component、@Bean注册为 BeanDefinition——此时只是"图纸" - 步骤 11:实例化全部非懒加载单例——图纸变成真正的对象
Bean 加载原理
从配置类到 BeanDefinition
步骤 5 的执行者是 ConfigurationClassPostProcessor,整个 Spring 中最核心的 BeanDefinitionRegistryPostProcessor 实现:
1 | |
解析完成后,扫描到的组件与 @Bean 方法都以 BeanDefinition 的形式存入 DefaultListableBeanFactory。容器此时持有的是"图纸集合",还没有任何业务对象。
Bean 生命周期
步骤 11 中,每个单例 Bean 经历四个阶段:
三个容易被忽略的细节:
@Autowired的解析者是AutowiredAnnotationBeanPostProcessor,注入发生在属性赋值阶段,而不是实例化阶段- AOP 代理在初始化的最后一步创建,因此代理对象身上的
@PostConstruct早已执行完毕 - 循环依赖的破局点在实例化与属性赋值之间:提前暴露的早期引用由
getEarlyBeanReference()提供,这就是三级缓存的由来
后置处理器家族
生命周期的每一步都由后置处理器驱动:
| 接口 | 扩展时机 | 典型用途 |
|---|---|---|
| BeanPostProcessor | 初始化前后 | 代理创建、回调触发 |
| InstantiationAwareBeanPostProcessor | 实例化前后、属性赋值前 | 修改注入逻辑 |
| MergedBeanDefinitionPostProcessor | 合并 BeanDefinition 后 | 收集注入元数据 |
| SmartInstantiationAwareBeanPostProcessor | 构造器推断、提前暴露引用 | 三级缓存 |
Spring 内置的关键实现:
| 实现类 | 职责 |
|---|---|
| AutowiredAnnotationBeanPostProcessor | @Autowired、@Value 注入 |
| CommonAnnotationBeanPostProcessor | @Resource、@PostConstruct、@PreDestroy |
| AbstractAutoProxyCreator | AOP 代理创建基类 |
| AsyncAnnotationBeanPostProcessor | @Async 异步代理 |
| InfrastructureAdvisorAutoProxyCreator | @Transactional 事务代理 |
自动配置原理
@EnableAutoConfiguration
1 | |
AutoConfigurationImportSelector 实现了 DeferredImportSelector——延迟到所有常规配置类解析完毕后才执行,保证用户的 Bean 定义先于自动配置注册。这是"用户配置永远优先"的实现基础。
Spring Boot 2.7 之前候选列表存放在 META-INF/spring.factories;之后迁移到 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,一行一个全限定类名。spring.factories 如今只负责加载监听器、初始化器等基础设施组件,4.x 依然保留这一职责。需要注意的是 4.x 迁移了部分类的包名——如 EnvironmentPostProcessor 从 org.springframework.boot.env 移到 org.springframework.boot,BootstrapRegistry 系列移到 org.springframework.boot.bootstrap——做深度集成的框架要同步更新 spring.factories 中的键。
条件注解
1 | |
三类最常用的条件:
@ConditionalOnClass:类路径存在指定类才生效,自动配置的第一道门槛——依赖不在,整类跳过@ConditionalOnMissingBean:容器中没有同类 Bean 才生效,用户自定义的配置永远赢@ConditionalOnProperty:配置属性满足要求才生效
所有条件注解都由 @Conditional(OnClassCondition.class) 这类元注解驱动,条件评估统一收敛在 SpringBootCondition:
1 | |
每个 @ConditionalOnXxx 对应一个 Condition 实现,把判断逻辑写在 getMatchOutcome() 里。评估结果写入 ConditionEvaluationReport,--debug 启动时打印的正是这份报告。
容器继承体系
两大接口体系
BeanFactory 是容器底层,ApplicationContext 是它的门面封装:
1 | |
ApplicationContext 不重复造轮子:它内部持有一个 DefaultListableBeanFactory,所有 Bean 操作委托给它,自己只叠加事件发布、资源加载、国际化能力。
BeanDefinition 体系
1 | |
图中省略了交叉继承:ScannedGenericBeanDefinition 同时实现了 AnnotatedBeanDefinition。实例化之前,容器总是把 BeanDefinition 合并成 RootBeanDefinition 再使用。
贯穿始终的设计模式
- 模板方法:
refresh()在AbstractApplicationContext定死骨架,onRefresh()留给子类——Boot 正是靠它创建 WebServer - 观察者:事件机制贯穿启动全程,
ApplicationListener是解耦扩展的标准入口 - 策略:
WebApplicationType决定上下文实现,BeanPostProcessor家族决定各阶段行为 - 委托:ApplicationContext 把 Bean 操作委托给内部的 BeanFactory
- 工厂:
BeanFactory统一管理 Bean 从定义到销毁的全生命周期
调试技巧
1 | |
1 | |
1 | |
相关阅读
- Spring Boot Bean 使用规范:Bean 定义、注入、作用域的实践与陷阱
- Spring Boot 常用注解:注解速查与对比
- MyBatis 整合:自动配置在持久层的完整落地



