一、起源:Spring Boot 要解决什么问题
Spring Framework 本身是一套完备的基础框架,Bean 容器、AOP、事务、MVC 等核心能力全部齐备。但在实际启动一个新 Spring 项目时,开发者面对的是五道与业务无关、却必须依次完成的配置门槛:
| 门槛 | 需要手动处理的事项 | 常见问题 |
|---|---|---|
| 依赖选型 | Maven/Gradle 中引入 Spring Core、MVC、Jackson、日志、JDBC、校验等组件,手动匹配兼容版本 | 版本不兼容导致 NoSuchMethodError、ClassNotFoundException |
| 容器部署 | 本地安装 Tomcat,创建 WAR 目录结构,配置 context.xml,将 WAR 放入 webapps/ 再启动 | 部署流程长,开发、测试、生产各环境不一致 |
| 框架配置 | 编写 Spring XML 或 JavaConfig:DataSource、JdbcTemplate、事务管理器、ComponentScan、HandlerMapping、ViewResolver | 新项目初始配置动辄数百行,跨项目靠复制粘贴维持 |
| 三方集成 | MyBatis 的 SqlSessionFactoryBean / MapperScannerConfigurer、Redis 的 RedisTemplate、RabbitMQ 的 ConnectionFactory | 各中间件配置模板独立,遗漏任意一项即导致运行失败 |
| 运维配置 | 多环境 properties 拆分、日志路径规划、JMX 配置、监控端点、启动脚本 | 每个项目各有一套,运维成本随项目数线性增长 |
这五道门槛不产生业务价值,但每个新项目都必须从头做一遍。Spring Boot 的定位是 Spring 的开发脚手架——它不替代 Spring Framework,而是在其之上封装一层自动化机制,把上述门槛里 90% 的默认情况自动处理,开发者只在偏离默认值时显式配置。支撑这个目标的核心原则是 约定优于配置(Convention over Configuration)。
二、自动装配
2.1 自动装配的本质
自动装配的本质是:容器启动时,根据 classpath 中是否存在某类、容器中是否已有某个 Bean、配置项是否命中,条件化地决定是否注册一组默认 Bean。开发者无需手写配置类即可获得基础能力。
典型例子:引入 spring-boot-starter-web 后,Spring Boot 自动完成 DispatcherServlet 注册、HandlerMapping 配置、Jackson ObjectMapper 初始化、Tomcat 内嵌容器启动等一系列工作,项目可直接在 8080 端口提供 HTTP 服务。
2.2 @SpringBootApplication 的组合结构
@SpringBootApplication
├── @SpringBootConfiguration 本质是 @Configuration,标记 Spring Boot 的主配置类
├── @ComponentScan 默认扫描启动类所在包及其子包
└── @EnableAutoConfiguration
└── @Import(AutoConfigurationImportSelector.class)
└── 继承 DeferredImportSelector,
让候选自动配置在所有用户 @Configuration 解析完毕后再加载,
保证用户自定义 Bean 先于自动装配注册,
@ConditionalOnMissingBean 判断时用户 Bean 已可见
DeferredImportSelector 的延迟加载设计是关键:普通 @Import 在解析配置类时立即导入;DeferredImportSelector 延迟到全部 @Configuration 解析完才执行,确保用户自定义 Bean 拥有更高优先级,自动装配仅在"用户没做"时兜底。
2.3 完整调用链
@EnableAutoConfiguration
→ AutoConfigurationImportSelector.selectImports()
├─ getCandidateConfigurations()
│ ├── 从 META-INF/spring/org.springframework.boot.autoconfigure
│ │ .AutoConfiguration.imports 读取候选列表(Spring Boot 2.7+ 格式)
│ └── 兼容读取 META-INF/spring.factories(2.7 之前的格式)
│
├── 去重(removeDuplicates)
├── 按 exclusions(exclude 属性、spring.autoconfigure.exclude)剔除
├── 调用 AutoConfigurationImportFilter(OnClassCondition 等)
│ 批量评估 @ConditionalOnClass,筛掉 classpath 不满足的候选
├── 按 @AutoConfigureBefore / After / Order 排序
└── 剩余配置类全限定名作为 BeanDefinition 注册进容器
→ 注册时再评估 Bean 级别的 @Conditional(OnBean / OnProperty 等)
三、@Conditional 家族
条件注解是自动装配的判定机制。判定分两个阶段:加载阶段(配置类是否被读入)和 注册阶段(Bean 是否被注册)。
| 注解 | 判定依据 | 阶段 | 典型使用 |
|---|---|---|---|
| @ConditionalOnClass | classpath 存在/不存在指定类 | 加载 | 有 DataSource 类才启用数据源自动配置 |
| @ConditionalOnMissingClass | classpath 不存在指定类 | 加载 | 没有 WebFlux 时才走 Servlet Web 路径 |
| @ConditionalOnMissingBean | 容器中没有指定类型的 Bean 才装配 | 注册 | 用户没写 DataSource 就使用自动装配的 HikariCP |
| @ConditionalOnBean | 容器中已有指定类型 Bean 才装配 | 注册 | 有 DataSource 才装配 JdbcTemplate |
| @ConditionalOnProperty | Environment 中指定键值匹配条件 | 注册 | spring.main.web-application-type=reactive 才配 Netty |
| @ConditionalOnWebApplication | 当前为 Servlet / Reactive / 非 Web 环境 | 注册 | Servlet 环境才注册 DispatcherServlet |
| @ConditionalOnNotWebApplication | 当前非 Web 应用 | 注册 | 非 Web 环境注册默认 CommandLine 启动器 |
| @ConditionalOnSingleCandidate | 指定类型在容器中只有一个候选 Bean | 注册 | 多个 DataSource 时不自动选,抛出歧义 |
| @ConditionalOnJava | JDK 版本处于指定范围 | 注册 | JDK 21+ 才启用虚拟线程相关支持 |
| @ConditionalOnWarDeployment | 当前以 WAR 部署到外部容器 | 注册 | 外部部署时不启动内嵌容器 |
| @ConditionalOnResource | classpath 存在指定资源文件 | 注册 | 存在 logback.xml 时采用 Logback 配置 |
| @ConditionalOnExpression | SpEL 表达式结果为 true | 注册 | 多属性复合判定的兜底 |
@AutoConfigureBefore / @AutoConfigureAfter / @AutoConfigureOrder 用来控制自动配置类之间的加载顺序,避免 @ConditionalOnBean 判定时目标 Bean 尚未注册。
四、起步依赖(Starters)
4.1 Starter 的本质
Starter 本身不包含代码,只是一组依赖集合的聚合描述符(POM 中的 <dependencyManagement> 与传递依赖)。引入一个 Starter 等价于一次性引入该主题所需的全部依赖,且所有传递依赖的版本由 Spring Boot BOM(Bill of Materials)保证兼容,开发者不再处理版本冲突。
Starter 的命名约定:
- 官方 Starter:
spring-boot-starter-*,如spring-boot-starter-web、spring-boot-starter-data-jpa - 第三方 Starter:
*-spring-boot-starter,如mybatis-spring-boot-starter、druid-spring-boot-starter
4.2 BOM 机制
Spring Boot BOM(spring-boot-dependencies)在 Maven 中通过 <dependencyManagement> 或 Gradle 中通过 BOM plugin 统一声明数百个常用库的版本。项目引入依赖时可省略 <version>,由 BOM 管控的兼容版本自动生效。
各 Starter 的选型、包含组件与适用场景,见 Spring Boot Starter 汇总。
五、外部化配置体系
5.1 属性源优先级链
Spring Boot 把配置来源按优先级从高到低组织为统一的属性源链。高优先级的值覆盖低优先级的同名键:
命令行参数(--server.port=8080)
↓
SPRING_APPLICATION_JSON 环境变量内联 JSON
↓
ServletContext 初始化参数(web.xml / ServletContainerInitializer)
↓
JNDI(java:comp/env 中的属性)
↓
Java 系统属性(System.getProperties(),如 -Dserver.port=8080)
↓
操作系统环境变量(SERVER_PORT 映射到 server.port)
↓
RandomValuePropertySource(random.* 随机值)
↓
jar 包外部 application-{profile}.yml / .properties
↓
jar 包内部 application-{profile}.yml / .properties
↓
jar 包外部 application.yml / .properties
↓
jar 包内部 application.yml / .properties
↓
@PropertySource 标注的属性源
↓
SpringApplication.setDefaultProperties() 设置的默认值
5.2 属性绑定
| 方式 | 机制 | 适用场景 |
|---|---|---|
| @Value("${key}") | 构造时对字段逐条解析 SpEL 或占位符 | 单个零散键值、需要 SpEL 表达式 |
| @ConfigurationProperties(prefix="xxx") | 按前缀匹配后通过 setter 或构造器批量绑定至 POJO;支持生成元数据供 IDE 自动补全 | 一组关联配置(如数据库、线程池),可维护性更强 |
| Environment.getProperty("xxx") | 运行时动态从 Environment 读取 | 无法在 Bean 初始化阶段静态声明的动态取值 |
@ConfigurationProperties 支持松散绑定(Relaxed Binding):my-project.myValue、myProject.myValue、MYPROJECT_MYVALUE 均能绑定到 MyProjectProperties.myValue 字段,适配不同来源配置的命名风格。
全部核心配置项与绑定示例,见 Spring Boot application.yml 核心配置。
六、内嵌容器
6.1 部署模式的变革
传统部署链路:应用打 WAR 包 → 运维安装外部 Tomcat → 拷贝 WAR 到 webapps/ → 启动 Tomcat。开发、测试、生产各有一套 Tomcat 安装与配置流程。
Spring Boot 内嵌容器部署链路:应用打 Fat JAR → 任一装有 JRE 的机器执行 java -jar app.jar。部署操作统一为两步,环境差异收敛到配置文件。
6.2 容器实现对比
| 容器 | 内嵌 Artifact | 特点 |
|---|---|---|
| Tomcat(默认) | spring-boot-starter-tomcat | 社区最广,Servlet 规范参考实现,Spring Boot 默认 |
| Jetty | spring-boot-starter-jetty | 启动快、内存占用低,Handler 架构灵活 |
| Undertow | spring-boot-starter-undertow | 基于 XNIO,Web 服务性能优,JBoss 默认容器 |
| Netty(WebFlux) | spring-boot-starter-webflux → reactor-netty | 响应式非阻塞,非 Servlet 体系 |
切换内嵌容器通过排除默认容器 Starter、引入对应 Starter 完成,业务代码不受影响。
6.3 Servlet Web 应用上下文
Spring Boot 为 Servlet Web 创建 AnnotationConfigServletWebServerApplicationContext,在容器 refresh 阶段通过 ServletWebServerFactory 创建内嵌 Tomcat/Jetty/Undertow 实例、注册 DispatcherServlet,并注册 Filter 与 Listener。全部流程走 Spring Bean 生命周期,不依赖外部容器部署。
七、Profiles 环境隔离
7.1 机制概览
通过 spring.profiles.active 指定激活的 profile,配置文件 application-dev.yml / application-test.yml / application-prod.yml 与主配置合并加载。同时:
@Profile("dev")标注的@Configuration类或@Bean方法仅在对应 profile 激活时注册spring.profiles.group可将多个 profile 组成一组(如production = prod,db-prod,monitor-prod)
7.2 激活方式(优先级从高到低)
- 命令行参数:
--spring.profiles.active=prod - JVM 系统属性:
-Dspring.profiles.active=prod - 环境变量:
SPRING_PROFILES_ACTIVE=prod - 主配置
application.yml中的spring.profiles.active
Profiles 的配置文件命名规范、激活方式与使用示例,见 Spring Boot Profiles 环境隔离。
八、启动事件序列
Spring Boot 的启动过程由 Spring Framework 的事件机制贯穿,关键事件按时间顺序:
SpringApplication.run() 启动
↓
ApplicationStartingEvent
启动流程刚开始,尚未加载配置、尚未创建 ApplicationContext
仅用于极早期处理(如 SpringApplication 本身的属性调整)
↓
ApplicationEnvironmentPreparedEvent
Environment 已创建、属性源与 profiles 已解析
可在此追加自定义 PropertySource(如接入远程配置中心)或修改属性
↓
ApplicationContextInitializedEvent
ApplicationContext 实例创建完成,尚未加载 BeanDefinition
可通过 ApplicationContextInitializer 注入自定义 BeanFactoryPostProcessor 等
↓
ApplicationPreparedEvent
BeanDefinition 全部加载完成,refresh() 尚未调用
可对 BeanDefinition 做最后修改
↓
refreshContext() 执行 Spring 标准容器刷新
→ Bean 实例化、依赖注入、AOP 代理、自动装配条件评估
↓
ApplicationStartedEvent
Context 刷新完成,ApplicationRunner / CommandLineRunner 尚未调用
↓
执行 ApplicationRunner / CommandLineRunner
按 @Order 排序执行,适合缓存预热、数据初始化、上报注册中心
↓
ApplicationReadyEvent
启动全部完成,应用已可接收请求
适合发布"就绪"消息或告警
── 任意阶段抛出异常 ──
↓
ApplicationFailedEvent
启动失败,附带异常信息,用于告警与诊断
ApplicationRunner 接收 ApplicationArguments(封装了命令行参数);CommandLineRunner 接收原始 String... args。两者功能等价,选其一即可。
九、生产级端点:Actuator
Actuator 提供一组生产环境下可用的 HTTP 或 JMX 端点,用于监控、诊断与运维干预:
| 端点 | 作用 |
|---|---|
| /actuator/health | 健康检查(UP/DOWN 综合状态 + 各组件子状态,可自定义 HealthIndicator) |
| /actuator/info | 应用信息(构建版本、Git 提交号、自定义 info.* 属性) |
| /actuator/metrics | 运行时指标(JVM 内存、GC、线程池、HTTP 请求耗时、数据源连接池) |
| /actuator/beans | 容器中全部 Bean 的类型、来源、依赖关系清单 |
| /actuator/conditions | 自动装配条件评估报告(Positive / Negative 匹配结果,排障核心工具) |
| /actuator/configprops | 所有 @ConfigurationProperties Bean 的绑定值快照 |
| /actuator/env | Environment 的 PropertySource 与属性值快照(敏感信息自动脱敏) |
| /actuator/loggers | 包/类级别的日志级别清单,支持 POST 请求运行时修改 |
| /actuator/heapdump | 下载当前 JVM 堆转储文件(hprof) |
| /actuator/threaddump | 获取线程栈快照 |
| /actuator/scheduledtasks | 列出所有 @Scheduled 任务 |
| /actuator/mappings | HTTP 请求映射(@RequestMapping 路由与处理器) |
| /actuator/shutdown | 优雅关闭应用(默认禁用,需 management.endpoint.shutdown.enabled=true) |
端点暴露由 management.endpoints.web.exposure.include / exclude 控制;敏感端点建议结合 Spring Security 做 IP 白名单或鉴权。
十、DevTools 开发期辅助
spring-boot-devtools 在开发环境下提供几项加速能力,进入生产环境(如打包为 Fat JAR)自动禁用:
| 能力 | 机制 |
|---|---|
| 热重启(Restart) | classpath 下 class 资源变动时,自动重启 ApplicationContext。采用双 ClassLoader 机制:不变的三方库(base ClassLoader)与应用代码(restart ClassLoader)分离,重启时仅重建 restart ClassLoader,比冷启动快一个数量级 |
| LiveReload | 内嵌 LiveReload 服务器,静态资源或模板变动时通知浏览器自动刷新(需浏览器插件) |
| 模板缓存禁用 | 自动关闭 Thymeleaf/Freemarker 等模板引擎的缓存,修改后生效无需重启 |
| 全局配置 | ~/.spring-boot-devtools.properties 存放跨项目的通用开发配置 |
| 触发文件 | 配置 spring.devtools.restart.trigger-file 后,只有修改指定标记文件才触发重启,避免 IDE 保存时频繁重启 |
十一、Fat JAR 打包结构与启动时序
11.1 Fat JAR 内部结构
spring-boot-maven-plugin 的 repackage goal 将普通 JAR 重打包为可执行 Fat JAR:
app.jar
├── META-INF/
│ ├── MANIFEST.MF
│ │ Main-Class: org.springframework.boot.loader.launch.JarLauncher
│ │ Start-Class: com.example.demo.DemoApplication (用户编写的启动类)
│ └── spring-configuration-metadata.json @ConfigurationProperties 元数据
├── org/springframework/boot/loader/ Spring Boot 自定义类加载器代码
│ ├── launch/JarLauncher java -jar 时的实际入口
│ ├── launch/LaunchedURLClassLoader 负责加载 BOOT-INF/classes 与 lib/*
│ └── jar/ 从 JAR 内读取嵌套 JAR 条目的文件系统抽象
├── BOOT-INF/
│ ├── classes/ 用户业务代码编译后的 class 与资源
│ └── lib/ 全部依赖 JAR(以原始 JAR 形式存放)
└── META-INF/resources/ 静态资源(可被 Servlet 容器直接识别)
标准 JDK 的类加载器只能读 JAR 根目录下的 class 文件,无法直接读 JAR 内嵌套的 JAR。JarLauncher 创建专用的 LaunchedURLClassLoader 来解析 BOOT-INF/ 结构,再通过反射调用 MANIFEST 中声明的 Start-Class.main()。
11.2 完整启动时序
命令行执行 java -jar app.jar
↓
JarLauncher.main()
├── 注册可解析嵌套 JAR 条目的协议处理器
├── 构造 LaunchedURLClassLoader,把 BOOT-INF/classes 与 BOOT-INF/lib/*.jar 加入 URL 列表
├── 设置线程上下文类加载器为 LaunchedURLClassLoader
└── 反射调用 MANIFEST.MF 中 Start-Class 对应的 main()
↓
SpringApplication.run(DemoApplication.class, args)
├── 推断 Web 应用类型:Servlet / Reactive / None(根据 classpath 是否存在 DispatcherServlet / DispatcherHandler)
├── 加载 BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener(spring.factories / imports 文件)
├── 准备 Environment:加载所有属性源、加载 Profiles、绑定 SpringApplication 属性
├── 打印 Banner(可自定义 banner.txt / banner.gif / 编程式)
├── 创建对应类型的 ApplicationContext
├── prepareContext():
│ ├── 将 Environment 注入 Context
│ ├── 调用所有 ApplicationContextInitializer
│ ├── 注册启动类为 BeanDefinition
│ └── 发布 ApplicationPreparedEvent
├── refreshContext():
│ 调用 AbstractApplicationContext.refresh(),
│ 执行 BeanFactory 标准刷新(BeanDefinition → Bean 实例化 → AOP 代理 → 事件广播)
├── afterRefresh():调用所有 ApplicationRunner / CommandLineRunner
├── 发布 ApplicationReadyEvent(成功)或 ApplicationFailedEvent(失败)
└── 返回 ConfigurableApplicationContext 句柄
十二、扩展点接口
Spring Boot 的可定制能力通过以下扩展点暴露,全部基于 Spring Framework 的标准机制接入:
| 扩展点 | 生效时机 | 典型用法 |
|---|---|---|
| ApplicationContextInitializer | Context 创建后、refresh() 前 | 注入自定义 PropertySource、注册自定义 BeanFactoryPostProcessor、打印启动信息 |
| ApplicationListener | 生命周期事件发布时 | EnvironmentPrepared 时拉取远程配置中心、Starting/Ready 时上报监控、Failed 时发告警 |
| ApplicationRunner / CommandLineRunner | refresh() 完成后、ReadyEvent 之前 | 缓存预热、数据库初始化、向 Nacos/Eureka 注册、启动后业务自检 |
| SpringBootExceptionReporter | 捕获到启动异常时 | 自定义 FailureAnalysis 输出可诊断的结构化错误(如提示缺失某 Starter) |
| AutoConfigurationImportFilter | 自动装配候选加载后、条件评估前 | 自定义规则批量过滤不相关自动配置,缩短启动时间 |
| EnvironmentPostProcessor | Environment 准备完毕、Context 未创建时 | 接入 Apollo/Nacos/Spring Cloud Config 等配置中心,将远程配置注入属性源链首位 |
| FailureAnalyzer / AbstractFailureAnalyzer | 异常转换阶段 | 针对特定异常生成可读的分析报告与行动建议 |
十三、与 Spring Framework 的关系定位
Spring Boot 不修改 Spring Framework 层的任何功能语义。它的全部能力建立在 Spring Framework 提供的标准扩展点之上:
Spring Framework(核心能力不变)
↑ Spring Boot 仅以标准扩展点接入
├── BeanFactoryPostProcessor 机制
│ → AutoConfigurationImportSelector 批量追加 BeanDefinition
├── BeanPostProcessor 机制
│ → ConfigurationPropertiesBindingPostProcessor 完成属性绑定
├── ApplicationListener 机制
│ → 整套 Starting / EnvironmentPrepared / Ready / Failed 事件
├── WebApplicationContext 变体
│ → ServletWebServerApplicationContext 内嵌 Tomcat/Jetty/Undertow
├── Environment 抽象
│ → 多属性源优先级链、Profiles、RandomValuePropertySource
├── @Conditional 扩展
│ → 十多种条件注解支撑自动装配判定
└── BOM 版本管理
→ 只约束版本号,不改变组件行为
Spring Framework 的 IoC 容器、AOP、事务、扩展点等核心机制见 Spring Framework 核心体系。
Spring Boot 在企业级项目中的实践应用(项目组织、分层落地、横切关注点、测试、部署运维)见 企业级项目实践指导手册。
十四、总结
Spring Boot 的价值不在于提供新能力,而在于 降低 Spring 项目落地的启动成本。它通过自动装配把重复的默认配置收回框架,通过 Starter 把依赖冲突解决在 BOM 层,通过内嵌容器把部署流程压缩为 java -jar,通过 Profiles、Actuator 把多环境与监控能力标准化为框架内置机制。
这些能力没有一项是凭空实现的,全部复用了 Spring Framework 已有的 BeanFactoryPostProcessor、BeanPostProcessor、事件机制、Environment 抽象等扩展点,配合 @Conditional 家族的条件化判定、Fat JAR 的类加载器方案,组成一套自洽的自动化脚手架。理解了 Spring Boot 这一层,再看具体的 Starter 选型、配置项、Profile 细节,就能明确它们在整体结构中的位置,排障时也能快速定位到问题所在的层级。