一、起源: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-webspring-boot-starter-data-jpa
  • 第三方 Starter:*-spring-boot-starter,如 mybatis-spring-boot-starterdruid-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.myValuemyProject.myValueMYPROJECT_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 激活方式(优先级从高到低)

  1. 命令行参数:--spring.profiles.active=prod
  2. JVM 系统属性:-Dspring.profiles.active=prod
  3. 环境变量:SPRING_PROFILES_ACTIVE=prod
  4. 主配置 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-pluginrepackage 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 细节,就能明确它们在整体结构中的位置,排障时也能快速定位到问题所在的层级。