Spring Boot 从零到生产级学习总览
Spring Boot 不是一个替代 Spring 的新容器,也不是“加一个注解就会自动写业务代码”的魔法框架。它仍以 Spring IoC 容器为核心,只是在 Spring 之上补齐了依赖版本管理、约定式配置、条件化自动配置、可执行应用、内嵌 Web 容器、生产监控等工程能力。
学完本专栏后,你不应只会创建 Controller,而应能从 main 方法一直解释到端口开始监听,并能回答下面这些问题:
SpringApplication.run()为什么能启动一个完整 Web 服务?@SpringBootApplication展开后包含什么,分别在哪个阶段生效?- 引入一个 Starter 后,Bean 为什么会自动出现在容器中?
- 自动配置候选类从哪里来,条件注解何时判断,用户 Bean 为什么能让默认 Bean 退让?
application.yml、环境变量和命令行参数冲突时,最终值从哪里来?- 内嵌 Tomcat 在什么时候创建,HTTP 请求怎样进入 Controller?
- 应用启动慢、自动配置不生效、端口未监听、配置未绑定时,从哪里开始排查?
- Boot 2.7/JDK 8 和 Boot 3/Java 17 的边界是什么,老项目为什么不能照抄新项目代码?
一、学习目标与验收标准
| 层次 | 你需要做到什么 | 只会表面时的典型表现 |
|---|---|---|
| 入门 | 能创建 Boot 2.7/JDK 8 项目并写出一个接口 | 只会复制脚手架,不知道注解作用 |
| 原理 | 能讲清启动、配置、自动配置、Starter 和容器刷新链路 | 只会背“约定优于配置” |
| 工程 | 能正确分层、校验参数、处理异常、管理配置与日志 | 业务逻辑全部堆在 Controller |
| 生产 | 能接入健康检查、指标、优雅停机和启动诊断 | 线上出错只会重启服务 |
| 扩展 | 能根据生命周期阶段选择正确扩展点 | 所有初始化逻辑都放 @PostConstruct |
| 兼容 | 能区分 Boot 2/3、JDK 8/17、javax/jakarta | 复制代码后大量包名或字节码错误 |
完成全部学习后,用从零到精通验收清单逐项验收,而不是用“页面看完了”代替“真正掌握了”。
二、正确学习路线
flowchart TD
A["Java 与 Maven 基础"] --> B["Spring IoC 和 Bean 生命周期"]
B --> C["Spring Boot 基础应用"]
C --> D["SpringApplication 启动流程"]
D --> E["Environment 与配置绑定"]
E --> F["自动配置与条件注解"]
F --> G["Starter 与扩展点"]
G --> H["Web 请求与工程分层"]
H --> I["Actuator 与生产排查"]
I --> J["商业场景和面试验收"]建议按下面顺序阅读:
- 基础入门:认识启动类、Bean、Controller 和基本注解。
- 启动流程全过程:从
SpringApplication到ApplicationReadyEvent。 - 配置体系全过程:从配置源到
Environment、Binder和属性对象。 - 自动配置原理:候选类发现、条件判断、BeanDefinition 注册和失败诊断。
- Starter 全过程:依赖聚合、autoconfigure 模块和商业公共能力封装。
- 扩展点:按启动阶段选择 Environment、容器、Bean、Web 和监控扩展点。
- Web 项目工程实践:请求链路、分层、校验、异常和统一响应。
- Actuator 监控:健康检查、指标、启动分析、线程栈和安全边界。
- 生产监控与线上问题排查:日志、指标、Trace、告警、JVM、容器和典型故障 Runbook。
- Spring Boot Admin:集中展示多个实例的 Actuator 信息、接入方式与安全边界。
- 商业场景训练营:将知识组合到订单、采集和客户端 Starter 场景。
- 面试题:标准回答、追问和对应原理锚点。
短页面用于第一次建立概念,带“全过程”的页面负责深入原理。不要在浅页面背完一句定义后就跳去面试页。
三、Spring、Spring MVC 与 Spring Boot 到底是什么关系
| 技术 | 主要职责 | Spring Boot 是否替代它 |
|---|---|---|
| Spring Framework | IoC、依赖注入、AOP、事务、事件、资源与扩展点 | 不替代,Boot 内部仍使用它 |
| Spring MVC | Servlet Web 请求分发、参数解析、Controller 调用、响应转换 | 不替代,starter-web 默认装配它 |
| Spring Boot | 依赖管理、自动配置、启动封装、内嵌服务器、生产运维 | 在前两者之上做工程化整合 |
| Spring Cloud | 注册发现、配置中心、网关、调用和容错等分布式能力 | 不属于 Boot 核心,通常基于 Boot 构建 |
可以把关系理解成:
flowchart TD
A["业务代码"] --> B["Spring Boot 工程约定与自动配置"]
B --> C["Spring MVC Web 请求体系"]
B --> D["Spring IoC、AOP、事务与事件"]
C --> D
D --> E["JDK、Servlet 容器和第三方库"]如果没有 Spring Boot,你仍可直接使用 Spring Framework,但需要自己选择依赖版本、创建 Web 容器、注册 DispatcherServlet、配置 JSON 转换器、数据源和监控。Boot 的价值不是改变这些原理,而是把成熟的默认组合编码成可覆盖的自动配置。
四、Spring Boot 解决了什么问题
4.1 依赖版本难以协调
传统项目可能分别声明 Spring、Jackson、Tomcat、日志组件版本。版本不兼容时会出现 NoSuchMethodError、ClassNotFoundException。Boot 的依赖管理提供一组经过兼容性验证的版本组合。
但要注意:Starter 负责“引入哪些依赖”,依赖管理负责“这些依赖默认用什么版本”,自动配置负责“类路径和配置满足条件后创建哪些 Bean”。这三件事不是同一个概念。
4.2 重复配置太多
绝大多数 Servlet 项目都需要 Web 容器、DispatcherServlet、消息转换器、异常处理基础设施。Boot 根据 classpath、配置属性、应用类型和已有 BeanDefinition 判断是否提供默认实现。
4.3 部署依赖外部容器
Boot 可以将应用打成可执行 Jar,并在同一个 JVM 中创建内嵌 Tomcat、Jetty 或 Undertow。Jar 并不是“自己实现了 HTTP”,而是把服务器依赖和应用一起打包,由启动流程创建 WebServer。
4.4 缺少统一生产入口
Actuator 为健康检查、指标、配置诊断、线程栈和日志级别提供统一端点。它不是完整监控平台:通常还要配合 Prometheus、Grafana、日志平台和链路追踪系统。
五、Spring Boot 不会替你做什么
- 不会自动设计领域模型、事务边界和幂等方案。
- 不会因为使用了 Starter 就消除慢 SQL、线程池耗尽和缓存一致性问题。
- 不会自动保证配置安全;密钥写进 Git 仍会泄露。
- 不会让所有依赖版本任意混搭;越过 Boot BOM 强行升级仍可能冲突。
- 不会让 Bean 天生线程安全;单例 Service 的可变字段仍可能发生并发问题。
- 不会让
@Transactional在自调用、非代理对象或错误异常规则下必然生效。
“自动配置”只是在条件满足时注册一套默认 Bean,不等于自动完成业务设计。
六、版本边界:JDK 8存量线与Java 17+现代线并行学习
本知识库同时维护两条完整学习线:基础页以 JDK 8 + Boot 2.7 帮助读者理解大量企业存量系统;现代版本专题系统讲解 Java 17+ + Boot 3.x。二者都会说明原理和可运行 Demo,不会把现代技术只缩成一张差异表。
| 组合 | 最低要求与定位 | 代码差异重点 |
|---|---|---|
| JDK 7 + 早期 Spring/Boot | 只能用于理解历史项目;Spring Boot 1.x 曾支持部分 JDK 7 场景 | 无 Lambda、默认方法、java.time,现在不建议新建 |
| JDK 8 + Boot 2.7.x | 重要存量基线;Boot 2.7 支持 Java 8 | javax.servlet、javax.validation、javax.annotation |
| JDK 11/17 + Boot 2.7.x | 存量系统升级 JDK 的过渡组合 | 仍使用 javax 生态,关注模块和反射限制 |
| Java 17+ + Boot 3.x | 当前新项目主线之一 | Spring 6、Jakarta EE 9,包名迁移为 jakarta.* |
| Java 21 + 新版 Boot 3.x | 可使用虚拟线程等新能力 | 不是所有旧中间件、Agent、驱动都天然兼容 |
最常见迁移错误:
// Boot 2.7 / Spring 5 常见写法
import javax.validation.constraints.NotBlank;
import javax.servlet.Filter;
// Boot 3 / Spring 6 对应写法
import jakarta.validation.constraints.NotBlank;
import jakarta.servlet.Filter;这不是 IDE 自动导包的小问题,而是 Jakarta EE 命名空间迁移。只改一个 import 往往不够,Servlet 容器、验证器、JPA 和第三方依赖都必须使用相容版本。
完整版本边界、Spring Framework 6、Jakarta 二进制不兼容、Security 6、Observability、AOT、Cloud 版本矩阵和迁移 Runbook,请阅读 Java 17+ 与 Spring Boot 3.x。
七、第一个可运行 Demo:JDK 8 + Spring Boot 2.7
7.1 Maven 配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>boot-first-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>7.2 启动类
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}启动类应放在业务根包,例如 com.example.demo。默认组件扫描从启动类所在包向下进行;如果启动类放在 com.example.demo.bootstrap,位于 com.example.demo.order 的组件可能扫描不到。
7.3 Controller
package com.example.demo.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
@GetMapping("/api/greeting")
public Greeting greeting(@RequestParam(defaultValue = "beginner") String name) {
return new Greeting("hello, " + name);
}
public static class Greeting {
private final String message;
public Greeting(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
}运行:
mvn spring-boot:run访问:
GET http://localhost:8080/api/greeting?name=Java8预期响应:
{"message":"hello, Java8"}这里发生的事情远不止“调用一个 Java 方法”:Tomcat 读取 HTTP 字节,Filter 链放行请求,DispatcherServlet 选择处理器,参数解析器读取 name,Controller 返回对象,Jackson 消息转换器将对象序列化为 JSON。
八、SpringApplication.run() 总体过程
flowchart TD
A["main 调用 SpringApplication.run"] --> B["创建 SpringApplication"]
B --> C["推断 WebApplicationType"]
C --> D["准备监听器、Environment 和 Profile"]
D --> E["创建 ApplicationContext"]
E --> F["加载启动类并解析自动配置"]
F --> G["refresh 刷新 Spring 容器"]
G --> H["创建单例 Bean 和代理"]
H --> I["创建并启动内嵌 WebServer"]
I --> J["执行 Runner 并发布就绪事件"]每一步都解决不同问题:
| 阶段 | 输入 | 核心输出 | 失败时常见现象 |
|---|---|---|---|
创建 SpringApplication | 主启动类、classpath | 应用类型、初始化器和监听器 | Web 类型判断与预期不同 |
| 准备 Environment | 文件、环境变量、命令行 | 最终配置视图和激活 Profile | 端口、数据源或开关值错误 |
| 创建 ApplicationContext | Web 应用类型 | 对应的 Spring 容器实现 | 容器类型或 Web 栈不匹配 |
| 加载 BeanDefinition | 扫描、导入、自动配置 | Bean 的定义,不一定已有实例 | Bean 未注册、条件不匹配 |
refresh() | BeanDefinition | Bean、代理、事件系统、WebServer | 循环依赖、创建异常、端口冲突 |
| Runner 与就绪事件 | 已刷新容器 | 初始化结果和可接流量状态 | 启动很慢或就绪过早 |
完整源码链和断点位置见启动流程全过程。
九、自动配置为什么不是“扫描到什么就配什么”
以 Boot 2.7 为例,核心链路可以简化为:
flowchart TD
A["@SpringBootApplication"] --> B["@EnableAutoConfiguration"]
B --> C["AutoConfigurationImportSelector"]
C --> D["读取自动配置候选名单"]
D --> E["处理 exclude、去重和排序"]
E --> F["条件注解判断 classpath、属性和 Bean"]
F --> G{"条件是否满足"}
G -- "否" --> H["记录未匹配原因并跳过"]
G -- "是" --> I["注册配置类和 BeanDefinition"]
I --> J["refresh 阶段创建真正 Bean"]必须分清三个时间点:
- 找到候选自动配置类,不代表它一定生效。
- 条件满足并注册 BeanDefinition,不代表对象已经实例化。
- Bean 在
refresh()中创建成功,才代表能力真正可用。
例如 DataSourceAutoConfiguration 在 classpath 缺 JDBC 类、配置缺失、已有用户 Bean 或被显式排除时,结果都可能不同。使用 debug=true 或 /actuator/conditions 可以看到匹配原因,而不是靠猜。
详细条件判断时机、Boot 2/3 声明文件与自定义自动配置 Demo 见自动配置原理。
十、Starter、自动配置和 Bean 的关系
flowchart TD
A["业务项目引入 starter"] --> B["Maven 带入 SDK 和 autoconfigure"]
B --> C["Boot 发现自动配置声明"]
C --> D["条件注解决定是否注册默认 Bean"]
D --> E["业务代码注入并使用 Bean"]| 模块 | 推荐职责 | 不应该做什么 |
|---|---|---|
| SDK | 纯 Java 客户端、接口和模型 | 依赖完整 Boot 应用上下文 |
| autoconfigure | 属性类、条件、默认 Bean、健康检查和失败分析 | 塞入具体业务订单规则 |
| starter | 聚合依赖,提供一个清晰入口 | 把所有实现代码堆在 starter 中 |
| 业务应用 | 提供实际配置,必要时覆盖默认 Bean | 修改公共组件源码才能使用 |
如果业务方声明了自己的同类型 Bean,公共自动配置常借助 @ConditionalOnMissingBean 退让。这体现的是“合理默认值 + 用户可覆盖”,不是 Bean 被随机覆盖。
完整商业客户端 Starter Demo 见Starter 全过程。
十一、一次 HTTP 请求怎样穿过 Spring Boot
flowchart TD
A["客户端发送 HTTP 请求"] --> B["Tomcat Connector 接收字节"]
B --> C["Servlet Filter 链"]
C --> D["DispatcherServlet"]
D --> E["HandlerMapping 查找 Controller"]
E --> F["HandlerAdapter 与参数解析器"]
F --> G["Controller 参数校验和协议转换"]
G --> H["Service 执行业务和事务"]
H --> I["Mapper、Redis、MQ 或下游服务"]
I --> J["返回值处理与 Jackson 序列化"]
J --> K["Tomcat 写回 HTTP 响应"]Controller 只负责协议层工作:接收参数、触发校验、调用 Service、转换响应。事务边界和业务编排应放在 Service;数据访问放在 Mapper/Repository。否则常见后果包括事务失效、代码无法复用、异常处理重复和测试困难。
完整工程分层和错误示例见Web 项目工程实践,MVC 内部细节见Spring MVC 请求执行链。
十二、商业场景:医疗数据采集服务如何使用 Boot
假设服务需要定时从医院接口拉取数据、校验、落库并发送 MQ:
flowchart TD
A["Boot 加载医院连接配置"] --> B["自动配置创建采集客户端"]
B --> C["健康检查验证必要依赖"]
C --> D["调度器触发采集批次"]
D --> E["Service 编排校验和事务"]
E --> F["数据库保存批次与数据"]
F --> G["提交后发送消息或写本地消息表"]
G --> H["Actuator 暴露健康与业务指标"]这里 Boot 适合承担:
- 将医院地址、超时、重试和开关绑定到类型安全的配置对象;
- 通过 Starter 为不同服务提供统一客户端、健康检查和指标;
- 通过条件注解决定某个医院适配器是否启用;
- 通过 Actuator 和 Micrometer暴露成功率、耗时和待处理批次数;
- 通过优雅停机停止接收新任务并等待在途任务收口。
Boot 不负责替你决定“数据库事务与消息怎样保证一致”“重复数据如何幂等”“重试是否会压垮医院接口”。这些需要分布式事务、消息可靠性、限流和幂等知识共同完成。
十三、生产问题从哪里开始排查
13.1 应用启动失败
- 先找日志中最早的根异常,不要只看最后一层
Application run failed。 - 判断失败发生在 Environment、BeanDefinition、Bean 创建、WebServer 还是 Runner 阶段。
- 配置绑定失败看属性名、最终配置源、类型转换和校验信息。
- Bean 创建失败沿
Caused by找构造器、注入、初始化方法或代理异常。 - 端口问题检查
BindException、监听端口和容器网络映射。
13.2 自动配置没有生效
java -jar app.jar --debug按下面顺序检查:
- Starter 是否真正进入运行时 classpath。
- 自动配置声明文件路径和类名是否正确。
- 目标自动配置是否在条件报告中出现。
- 是否被
exclude排除。 @ConditionalOnClass、@ConditionalOnProperty等条件为何不满足。- 是否因为已有用户 Bean,触发了
@ConditionalOnMissingBean退让。 - BeanDefinition 已注册后,是否又在 Bean 创建或配置绑定阶段失败。
13.3 应用启动成功但接口不通
依次确认:端口是否监听、容器端口是否映射、上下文路径是否正确、Controller 是否被扫描、请求方法和路径是否匹配、Filter/Security 是否拦截、网关和反向代理是否转发到正确实例。
13.4 应用启动慢
重点检查 Bean 构造器、@PostConstruct、初始化方法、数据源连接、配置中心、类扫描、Runner 和外部系统调用。不要在 Bean 创建阶段做没有超时的网络请求;否则一个外部系统不可用就可能让整个服务无法启动。
具体端点和指标见Actuator 监控,CPU、内存、线程池、连接池、数据库和容器等完整 Runbook 见生产监控与线上问题排查。
十四、常见错误,以及为什么会错
| 错误 | 为什么会出问题 | 正确方向 |
|---|---|---|
| 启动类放在业务子包 | 默认扫描范围变窄,部分 Bean 不会注册 | 启动类放根包或显式配置扫描范围 |
| 在构造器中请求外部接口 | Bean 创建被网络阻塞,容器无法完成刷新 | 使用可超时、可观测的启动后预热 |
| 在 Runner 中解密配置 | Runner 执行时配置绑定和自动配置已经结束 | 使用更早的配置扩展机制或外部 Secret 系统 |
所有配置都用 @Value | 分散、无结构、类型与校验能力弱 | 一组业务配置使用 @ConfigurationProperties |
| 看到 Bean 不存在就立刻手写配置 | 可能只是条件不满足或被用户 Bean 顶掉 | 先看 conditions 报告和 Bean 创建异常 |
| 暴露全部 Actuator 端点 | 可能泄露环境变量、配置、线程栈和堆数据 | 最小暴露、内网访问、鉴权和脱敏 |
| Boot 2 项目复制 Boot 3 import | javax 与 jakarta 类型不兼容 | 先确认版本矩阵,再选择示例代码 |
十五、面试标准回答
问:Spring Boot 的核心原理是什么?
Spring Boot 仍然以 Spring IoC 容器为核心。
SpringApplication.run()先推断应用类型并准备 Environment,再创建 ApplicationContext;@SpringBootApplication触发组件扫描和自动配置导入,Boot 从声明文件读取候选自动配置类,经过排除、排序和条件注解判断后注册 BeanDefinition;容器在refresh()阶段执行后置处理器、创建单例 Bean 和代理。Servlet Web 应用还会在刷新过程中通过ServletWebServerFactory创建内嵌 Tomcat。最后执行 Runner、发布就绪事件。Starter 负责带入依赖和自动配置模块,用户 Bean 可以通过@ConditionalOnMissingBean机制覆盖默认实现。
追问时应继续展开:
@SpringBootApplication的三个核心组成;- Boot 2.6及更早、Boot 2.7过渡机制与 Boot 3
AutoConfiguration.imports; - 条件判断与 Bean 创建不是同一阶段;
- 自动配置排序不等于 Bean 实例化顺序;
Environment、配置绑定和最终属性来源;refresh()与内嵌 Tomcat 创建时机;- 自动配置未生效的 conditions 排查方法。
更多标准回答放在Spring Boot 面试题,每个回答应回到对应原理页理解,不能只背这一段。
十六、问题到知识点的快速跳转
| 你遇到的问题 | 应阅读的页面 |
|---|---|
| 不知道JDK 8/17、Boot 2/3如何选择和迁移 | Java 17+与Spring Boot 3.x |
不知道 run() 做了什么 | 启动流程全过程 |
| 不知道某个配置最终取了哪个值 | 配置体系全过程 |
| Bean 没有被自动创建 | 自动配置原理 |
| 想把公共客户端做成开箱即用组件 | Starter 全过程 |
| 不知道该选 Runner、BPP、Filter 还是 Interceptor | Spring Boot 扩展点 |
| Controller、Service、Mapper 职责混乱 | Web 项目工程实践 |
| 要做健康检查、指标或线程诊断 | Actuator 监控 |
| 线上接口慢、CPU高、OOM、连接池耗尽怎么处理 | 生产监控与线上问题排查 |
| 想集中查看和管理多个Spring Boot实例 | Spring Boot Admin |
| 要练完整商业组合场景 | 商业场景训练营 |
| 要检查自己是否真正掌握 | 从零到精通验收清单 |
| 要准备面试标准回答 | 面试题 |
本章小结
Spring Boot 的本质可以归纳为一条完整链路:
依赖管理提供兼容版本,Starter 把场景依赖带入 classpath,启动流程准备配置和容器,自动配置根据条件注册默认 Bean,Spring
refresh()创建 Bean 与代理,WebServer 开始监听端口,Actuator 提供生产可观测入口;业务代码仍需自己设计分层、事务、一致性、并发和失败处理。
当你能解释这条链路中每一步的输入、输出、时机和失败现象时,才算真正理解 Spring Boot,而不只是会使用脚手架。
