本文基于 Spring Boot 4.1.2 官方文档编写,分成「教程篇」和「原理篇」:教程篇从装 JDK 开始,带你做一个能跑、能测、能部署的订单服务;原理篇再拆自动配置、Bean 生命周期、Web、数据、事务、安全、可观测性和测试。没有 Spring Boot 经验也能跟着做。
- 教程篇:从 0 到 1 做订单服务
- 原理篇
- 先看全局:Spring Boot 是什么
- 从 main() 到一次 HTTP 请求
- 组件地图
- 注解词典
- 版本与基线
- 项目结构与启动
- 自动配置原理
- 配置体系
- Bean 生命周期与依赖注入
- Web 开发
- HTTP 客户端
- 数据访问
- 事务与并发
- 安全
- Actuator 与可观测性
- 测试
- AOT 与原生镜像
- 打包与部署
- 性能与调优
- 常见坑
- 企业实践
- 附录
教程篇:从 0 到 1 做订单服务
这一篇按顺序做,不要跳步。每一步都有代码、命令和预期结果;报错时先看第 13 节。
0. 你将做出什么
最终你会有一个订单服务:
POST /api/orders创建订单GET /api/orders/{id}查询订单- PostgreSQL 持久化 + Flyway 建表
- 参数校验、统一错误响应
- Basic Auth 保护接口
- Actuator 健康检查
- 单元/切片/集成测试
- 可执行 jar 和 Docker 镜像
技术栈:
- Spring Boot 4.1.x
- Java 17+(教程用 Java 25 LTS)
- Maven
- PostgreSQL 17
- Docker
1. 准备环境
安装并验证:
java -version |
预期:
- Java 17 或更高
- Maven 3.6.3 或更高
- Docker 可用
如果 java -version 不是 17+,先装 JDK 25 LTS。不要用 IDE 自带的老 JDK 运行。
2. 创建项目
打开 https://start.spring.io,选择:
- Project:Maven
- Language:Java
- Spring Boot:4.1.x
- Java:25
- Group:
com.example - Artifact:
orders - Dependencies:
- Spring Web
- Validation
- Spring Data JPA
- PostgreSQL Driver
- Flyway Migration
- Spring Boot Actuator
- Testcontainers
生成并解压,或用命令行:
curl -G https://start.spring.io/starter.zip \ |
初始结构:
orders/ |
pom.xml 里最重要的是:
<parent> |
parent 负责依赖版本管理;starter 负责把一组依赖打包给你。
3. 第一次启动
把 application.properties 改名为 application.yaml(也可以保留 properties),先不配数据库:
spring: |
启动:
./mvnw spring-boot:run |
Windows 用:
mvnw.cmd spring-boot:run |
预期日志:
Tomcat started on port 8080 (http) |
验证:
curl -i http://localhost:8080/actuator/health |
预期:
{"status":"UP"} |
如果 404,检查 spring-boot-starter-actuator 是否在 pom.xml;如果 401,说明你提前加了 Security,先移除或看第 9 节。
停止:Ctrl+C。
4. 第一个接口
先不接数据库,返回一个假订单,确认 Web 链路通。
src/main/java/com/example/orders/api/CreateOrderRequest.java:
package com.example.orders.api; |
OrderResponse.java:
package com.example.orders.api; |
OrderController.java:
package com.example.orders.api; |
重启后调用:
curl -i -X POST http://localhost:8080/api/orders \ |
预期:
201 |
查询:
curl http://localhost:8080/api/orders/1 |
这一步发生了什么:
@RestController把类注册成 Bean,并把返回值写进响应体@RequestMapping("/api/orders")给类加统一前缀@PostMapping映射 POST 请求@RequestBody用 Jackson 3 把 JSON 转成CreateOrderRequest@Valid触发 Jakarta Validation@PathVariable把 URL 里的{id}绑定到参数
5. 参数校验与统一错误
请求体加了 @NotNull、@DecimalMin,但校验失败默认返回 400,结构不统一。加一个全局处理器。
src/main/java/com/example/orders/api/ApiExceptionHandler.java:
package com.example.orders.api; |
测试非法请求:
curl -i -X POST http://localhost:8080/api/orders \ |
预期 400,响应是 application/problem+json,包含 errors。
解释:
@RestControllerAdvice对所有 Controller 生效@ExceptionHandler指定处理的异常类型ProblemDetail是 RFC 9457 标准错误结构,比自定义{code,msg}更通用
6. 连接 PostgreSQL
先启动数据库。compose.yaml:
services: |
启动:
docker compose up -d |
application.yaml 加数据源:
spring: |
建表脚本 src/main/resources/db/migration/V1__create_orders.sql:
create table orders ( |
重启应用。预期日志出现 Flyway 迁移成功,Hibernate 校验通过。
如果报 Connection refused:数据库没起或端口不对。
如果报 Flyway validate failed:不要改已经执行过的迁移文件,新增 V2__...sql。
7. 用 JPA 读写订单
src/main/java/com/example/orders/domain/Order.java:
package com.example.orders.domain; |
OrderStatus.java:
package com.example.orders.domain; |
OrderRepository.java:
package com.example.orders.domain; |
OrderService.java:
package com.example.orders.service; |
改 Controller,注入 Service:
package com.example.orders.api; |
重启并测试:
curl -i -X POST http://localhost:8080/api/orders \ |
这次 id 来自数据库,重启后数据仍在。
解释:
@Entity告诉 JPA 这是表映射@Id+@GeneratedValue声明主键和生成策略@Version做乐观锁@Transactional在 Service 外层开事务;Controller 不要直接写事务JpaRepository由 Spring Data 自动生成实现,不需要写实现类
8. 配置属性
src/main/java/com/example/orders/config/OrderProperties.java:
package com.example.orders.config; |
在启动类加 @ConfigurationPropertiesScan:
|
application.yaml:
app: |
在 Service 里注入即可:
在 `OrderService` 构造器里注入 `OrderProperties` 即可,Spring Boot 会按 `app.order.*` 自动绑定。 |
解释:
@ConfigurationProperties把一组配置绑定到对象@Validated+ Jakarta Validation 在启动时校验- record 用构造器绑定,配置缺失会启动失败,比运行时才发现好
9. 加安全认证
加依赖:
<dependency> |
SecurityConfig.java:
package com.example.orders.config; |
调用:
curl -u demo:demo http://localhost:8080/api/orders/1 |
不带认证会返回 401。
说明:
SecurityFilterChain是 Security 7 的配置方式authorizeHttpRequests控制 URL 权限PasswordEncoder用 BCrypt,不要明文存密码- 教程用内存用户;生产用 OAuth2/JWT 或数据库用户
10. Actuator 健康检查
application.yaml:
management: |
访问:
curl -s http://localhost:8080/actuator/health |
- liveness:进程是否活着,不要包含外部依赖
- readiness:是否可以接流量,包含数据库等关键依赖
- 生产不要把
env、heapdump暴露到公网
11. 写测试
11.1 Web 切片测试
package com.example.orders.api; |
11.2 数据层 + Testcontainers
package com.example.orders.domain; |
11.3 启动测试
./mvnw test |
预期:BUILD SUCCESS。
常见问题:
- Docker 没启动,Testcontainers 起不来
- 上下文缓存被不同配置破坏,测试变慢
@MockitoBean替代旧的@MockBean
12. 打包与部署
打包:
./mvnw clean package |
Dockerfile:
FROM eclipse-temurin:25-jre |
构建运行:
docker build -t orders:1.0 . |
Linux 上 host.docker.internal 默认不可用,改用宿主机 IP 或 --network host。
Kubernetes 探针和优雅停机:
livenessProbe: |
server: |
13. 常见错误速查
| 现象 | 原因 | 处理 |
|---|---|---|
启动报 UnsupportedClassVersionError |
JDK 太老 | 换 Java 17+ |
| 端口 8080 被占用 | 已有进程 | server.port 换端口 |
/actuator/health 404 |
没加 actuator | 加 starter |
| 401 | Security 默认保护 | 配 SecurityFilterChain 或带认证 |
400 + problem+json |
参数校验失败 | 看 errors |
| 415 | Content-Type 不对 | 加 application/json |
Connection refused |
数据库没起 | docker compose up -d |
No default constructor |
JPA 实体缺无参构造 | 加 protected Order() {} |
ddl-auto=validate 失败 |
表结构和实体不一致 | 改 Flyway 脚本 |
| Flyway checksum mismatch | 改了已执行脚本 | 新增 V2,不要改 V1 |
| Testcontainers 起不来 | Docker 未启动/无权限 | 启动 Docker/检查 socket |
| 测试上下文反复重建 | 配置不同/@MockBean 不同 |
统一测试配置 |
14. 下一步
跟做完这一篇,你已经掌握:
- 项目创建、启动、配置
- REST 接口、参数校验、统一错误
- JPA、事务、Flyway
- 配置属性、安全、Actuator
- 测试、打包、Docker
接下来读「原理篇」:
- 自动配置为什么生效
- Bean 生命周期和代理
- 配置优先级
- 事务失效场景
- 测试切片
- 原生镜像
原理篇
先看全局:Spring Boot 是什么
没有 Spring Boot 时,你要做什么
用原生 Spring Framework 写一个 Web 服务,通常要手动做这些事:
- 引入 Spring MVC、Jackson、Tomcat、日志、校验等一堆依赖,并解决版本冲突
- 创建
ApplicationContext - 配置
DispatcherServlet - 启动内嵌 Tomcat 或部署 WAR
- 配置 JSON 转换器、异常处理、静态资源
- 配置数据源、连接池、事务管理器
- 写启动脚本、健康检查、监控
Spring Boot 把这些“标准答案”固化下来:你加依赖、写少量配置、定义 Bean,剩下的它按约定装配好。
Spring Boot 的三层能力
+------------------------------------------------------+ |
一句话:Spring Boot = Spring Framework + 约定好的默认配置 + 内嵌运行时 + 生产能力。
你只需要关心三件事
- 依赖:需要什么能力,加对应 starter
- 配置:哪些默认值不适用,用
application.yaml或环境变量覆盖 - Bean:哪些对象需要自己定义,用
@Bean/@Component声明
其他事情,先假设 Spring Boot 已经做对了;不生效时再用 --debug 看条件报告。
约定优于配置
例如:
- 把
application.yaml放在src/main/resources,Boot 自动加载 - 启动类在根包,Boot 自动扫描子包
- 加
spring-boot-starter-web,Boot 自动启动 Tomcat 并注册DispatcherServlet - 配置了
spring.datasource.url,Boot 自动创建DataSource - 加
spring-boot-starter-actuator,自动暴露健康检查和指标
约定不是魔法,背后都是自动配置类和条件注解;后面会拆开讲。
从 main() 到一次 HTTP 请求
先跟着一条请求走一遍,建立整体地图。
启动阶段
|
SpringApplication.run 大致做这些事:
- 准备 Environment:加载
application.yaml、环境变量、命令行参数、profile - 创建 ApplicationContext:默认是
AnnotationConfigServletWebServerApplicationContext - 扫描 BeanDefinition:
@ComponentScan找到@Component/@Service/@Repository/@Controller - 执行自动配置:读取
AutoConfiguration.imports,按条件注册 Bean - refresh:实例化 Bean、依赖注入、执行
BeanPostProcessor、创建 AOP 代理 - 启动内嵌容器:Tomcat/Jetty 开始监听端口
- 发布事件:
ApplicationReadyEvent,应用可用
关键点:
Environment先于 Bean 创建,所以配置能影响自动配置BeanDefinition只是“配方”,refresh 时才真正创建对象- AOP 代理在
BeanPostProcessor阶段生成,所以自调用会绕过代理
请求阶段
浏览器/客户端 |
每一层对应一个组件:
- Tomcat:内嵌 Servlet 容器
- Filter:Servlet 规范,Spring Security 就在这一层
- DispatcherServlet:Spring MVC 的入口
- HandlerMapping:根据 URL 找 handler
- HandlerAdapter:负责调用 handler,处理
@RequestParam、@RequestBody、@Valid - HttpMessageConverter:Jackson 3 负责 JSON
@ControllerAdvice:异常处理DataSource/EntityManager:数据访问@Transactional:AOP 代理在 Service 方法外层开事务
你写的代码在哪一层
| 你写的 | 所在层 | 职责 |
|---|---|---|
@RestController |
Web 层 | 接收 HTTP、返回响应 |
@Service |
业务层 | 业务逻辑、事务边界 |
@Repository |
数据层 | 数据访问 |
@Entity |
领域模型 | 映射数据库表 |
@Configuration |
配置层 | 声明 Bean |
@ConfigurationProperties |
配置层 | 绑定配置 |
@ControllerAdvice |
Web 层 | 全局异常/绑定 |
Filter |
Servlet 层 | 请求前后处理 |
@Aspect |
横切层 | 日志、权限、事务等 |
组件地图
| 组件 | 负责什么 | 被谁调用 | 在哪里见到 |
|---|---|---|---|
spring-boot-starter-* |
一组依赖的集合 | Maven/Gradle | pom.xml |
spring-boot-autoconfigure |
自动配置类 | Spring Boot | 依赖树、--debug |
AutoConfiguration.imports |
自动配置注册表 | @EnableAutoConfiguration |
META-INF/spring/ |
ApplicationContext |
IoC 容器 | SpringApplication |
启动日志、@Autowired |
BeanFactory |
Bean 的底层工厂 | ApplicationContext |
很少直接接触 |
BeanDefinition |
Bean 的“配方” | 容器 | @Bean/扫描 |
BeanPostProcessor |
Bean 创建前后扩展 | 容器 | AOP、@Async |
Environment |
配置和 profile | 容器 | application.yaml |
DispatcherServlet |
MVC 前端控制器 | Tomcat | Web 请求入口 |
HandlerMapping |
URL → handler | DispatcherServlet |
@RequestMapping |
HandlerAdapter |
调用 handler | DispatcherServlet |
参数绑定/校验 |
HttpMessageConverter |
对象 ↔ JSON | MVC | Jackson 3 |
Filter |
Servlet 层拦截 | Tomcat | Security、CORS |
HandlerInterceptor |
MVC 层拦截 | DispatcherServlet |
权限、审计 |
DataSource |
数据库连接 | JPA/JDBC | HikariCP |
EntityManager |
JPA 持久化上下文 | Repository | Hibernate 7 |
PlatformTransactionManager |
事务管理 | @Transactional |
事务代理 |
Actuator |
健康、指标、端点 | 运维/监控 | /actuator/* |
Micrometer |
指标门面 | Actuator/应用 | MeterRegistry |
Spring TestContext |
测试上下文缓存 | 测试框架 | @SpringBootTest |
AOT |
启动时生成代码 | 构建/运行 | native image |
spring-boot-maven-plugin |
打包/运行/镜像 | Maven | mvn spring-boot:* |
注解词典
按“用在哪里、解决什么问题”分组。初学时先记住每组 2–3 个核心注解,其余用到再查。
启动与容器
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@SpringBootApplication |
组合启动注解 | 启动类 | 只能有一个 |
@SpringBootConfiguration |
标记配置类 | 启动类 | 被组合,很少单独用 |
@EnableAutoConfiguration |
开启自动配置 | 配置类 | 排除自动配置时用 |
@ComponentScan |
扫描组件 | 配置类 | 默认扫描当前包及子包 |
@Configuration |
声明配置类 | 类 | proxyBeanMethods=false 可减代理 |
@Bean |
声明 Bean | 方法 | 方法名默认是 Bean 名 |
@Import |
导入配置/组件 | 配置类 | 不是扫描,是显式导入 |
@Component |
通用组件 | 类 | 语义不如 @Service 清晰 |
@Service |
业务组件 | 类 | 只是语义标记 |
@Repository |
数据访问组件 | 类 | 会做异常转换 |
@Controller |
MVC 控制器 | 类 | 返回视图 |
@RestController |
REST 控制器 | 类 | = @Controller + @ResponseBody |
@ConfigurationProperties |
绑定配置 | 类/record | 需要启用扫描或注册 |
@EnableConfigurationProperties |
注册配置类 | 配置类 | 非扫描场景用 |
@ConfigurationPropertiesScan |
扫描配置类 | 启动类 | 和 @Component 二选一 |
@Validated |
开启校验 | 类/方法 | 分组校验要配合 @Valid |
@Profile |
按环境启用 | 类/方法 | profile 覆盖顺序 |
@ConditionalOn* |
条件装配 | 配置类/Bean | 条件不满足时静默跳过 |
@AutoConfiguration |
自动配置类 | 类 | 需要注册到 imports 文件 |
@AutoConfigureBefore |
自动配置顺序 | 类 | 不要依赖扫描顺序 |
@AutoConfigureAfter |
自动配置顺序 | 类 | 同上 |
@Scope |
Bean 作用域 | 类/方法 | singleton 注入 prototype 只注入一次 |
@Lazy |
延迟初始化 | 类/方法 | 启动快,但首次访问慢 |
@Primary |
同类型首选 | 类/方法 | 多实现时用 |
@Qualifier |
指定 Bean | 注入点 | 和 @Primary 配合 |
@DependsOn |
依赖初始化顺序 | 类/方法 | 尽量用构造器依赖代替 |
@Order |
排序 | 类/方法 | 只影响有序集合 |
Web
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@RequestMapping |
映射 URL | 类/方法 | 方法级会继承类级 |
@GetMapping/@PostMapping 等 |
指定 HTTP 方法 | 方法 | 不要用 @RequestMapping 代替所有 |
@PathVariable |
取路径变量 | 参数 | 名称要和 {} 一致 |
@RequestParam |
取查询参数 | 参数 | required=false 要显式 |
@RequestBody |
取请求体 | 参数 | 依赖 HttpMessageConverter |
@ResponseBody |
返回值写响应体 | 方法/类 | @RestController 已包含 |
@ResponseStatus |
指定状态码 | 方法/异常 | 异常处理时更常用 ProblemDetail |
@ControllerAdvice |
全局增强 | 类 | 只对 MVC 生效 |
@RestControllerAdvice |
全局 REST 增强 | 类 | = @ControllerAdvice + @ResponseBody |
@ExceptionHandler |
处理异常 | 方法 | 匹配最具体的异常 |
@CrossOrigin |
跨域 | 类/方法 | 生产用全局 CORS 配置 |
@Valid |
触发校验 | 参数 | 嵌套对象要加 @Valid |
@Validated |
方法级校验 | 类/方法 | 分组校验 |
@HttpExchange |
HTTP 接口 | 接口/方法 | 客户端/服务端可共用 |
@GetExchange/@PostExchange |
指定方法 | 方法 | 与 @HttpExchange 配合 |
@ImportHttpServices |
注册 HTTP 客户端组 | 配置类 | Spring Framework 7 的写法 |
数据与事务
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@Entity |
JPA 实体 | 类 | 需要无参构造 |
@Table |
指定表名 | 类 | 默认类名 |
@Id |
主键 | 字段 | 必须有 |
@GeneratedValue |
主键生成 | 字段 | 策略要和数据库匹配 |
@Column |
列映射 | 字段 | 不要和 @Entity 字段名冲突 |
@Version |
乐观锁 | 字段 | 更新要处理冲突 |
@OneToMany/@ManyToOne |
关系映射 | 字段 | 注意 N+1 和懒加载 |
@JoinColumn |
外键列 | 字段 | 双向关系要维护一致 |
@Repository |
数据访问 | 接口/类 | Spring Data 接口可省略 |
@Query |
自定义 JPQL/SQL | 方法 | 原生 SQL 要 nativeQuery=true |
@Modifying |
更新/删除查询 | 方法 | 需要事务 |
@EntityGraph |
抓取策略 | 方法 | 解决 N+1 |
@Transactional |
事务 | 类/方法 | 自调用失效、异常回滚规则 |
@TransactionalEventListener |
事务事件 | 方法 | 事务提交后执行 |
AOP、异步、缓存、调度
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@Aspect |
切面 | 类 | 需要 AOP 依赖 |
@Pointcut |
切点 | 方法 | 表达式要可维护 |
@Before/@After/@Around |
通知 | 方法 | @Around 要 proceed() |
@Async |
异步执行 | 方法 | 需要 @EnableAsync,自调用失效 |
@EnableAsync |
开启异步 | 配置类 | 自定义线程池 |
@Scheduled |
定时任务 | 方法 | 多实例会重复执行 |
@EnableScheduling |
开启调度 | 配置类 | 线程池大小 |
@Cacheable |
缓存读 | 方法 | 需要 @EnableCaching |
@CacheEvict |
缓存失效 | 方法 | key 设计要一致 |
@EnableCaching |
开启缓存 | 配置类 | 需要 CacheManager |
安全
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@EnableWebSecurity |
开启 Web 安全 | 配置类 | Spring Boot 常自动配置 |
@EnableMethodSecurity |
开启方法安全 | 配置类 | 默认关闭 |
@PreAuthorize |
调用前鉴权 | 方法 | 表达式要可读 |
@PostAuthorize |
返回后鉴权 | 方法 | 性能与语义 |
@Secured |
角色鉴权 | 方法 | 不支持复杂表达式 |
可观测性
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@Timed |
记录耗时 | 方法 | 需要 Micrometer |
@Counted |
计数 | 方法 | 指标基数 |
@Observed |
Observation | 方法 | 需要开启注解扫描 |
测试
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@SpringBootTest |
完整上下文 | 类 | 慢,尽量复用上下文 |
@WebMvcTest |
MVC 切片 | 类 | 不加载 Service/Repository |
@DataJpaTest |
JPA 切片 | 类 | 默认回滚 |
@JsonTest |
JSON 切片 | 类 | 只测序列化 |
@RestClientTest |
HTTP 客户端切片 | 类 | mock server |
@MockitoBean |
替换 Bean | 字段 | Spring Boot 3.4+ |
@MockitoSpyBean |
Spy Bean | 字段 | 同上 |
@Testcontainers |
容器测试 | 类 | 需要 Docker |
@Container |
声明容器 | 字段 | 生命周期 |
@ServiceConnection |
自动连接容器 | 字段 | 需要对应模块 |
@DynamicPropertySource |
动态配置 | 静态方法 | 在上下文启动前执行 |
@TestPropertySource |
测试配置 | 类 | 覆盖优先级 |
原生镜像
| 注解 | 作用 | 放在哪 | 常见坑 |
|---|---|---|---|
@RegisterReflectionForBinding |
注册反射 | 类 | 只在 native 需要 |
@ImportRuntimeHints |
导入 hints | 配置类 | 动态反射要自己写 |
版本与基线
- Spring Boot 4.1.2(官方文档当前版本)
- Spring Framework 7.0.10+
- **Java 17+**,兼容到 Java 27
- Maven 3.6.3+;Gradle 8.14+/9.x
- 内嵌容器:Tomcat 11.0.x、Jetty 12.1.x,Servlet 6.1
- 默认 JSON:Jackson 3
- ORM:Hibernate 7.2
- 监控:Micrometer 1.16+、Micrometer Tracing
- 安全:Spring Security 7
Spring Boot 4 的重要变化:
- Jakarta EE 11,
javax.*彻底不再适用 - Jackson 3 成为默认,Jackson 2 以废弃形式保留
- 自动配置类不再暴露 public 成员
- HTTP Service Client 自动配置
- 新增
spring-boot-starter-opentelemetry spring.threads.virtual.enabled=true可启用虚拟线程management.tracing.enabled改名management.tracing.export.enabledspring.dao.exceptiontranslation.enabled改名spring.persistence.exceptiontranslation.enabled
项目结构与启动
典型结构:
src/main/java/com/example/app |
启动类:
|
@SpringBootApplication 组合了:
@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan
默认扫描启动类所在包及子包。不要把启动类放在过高层级,避免扫描无关模块。
启动流程简化:
SpringApplication.run |
自动配置原理
自动配置的目标:根据 classpath、已有 Bean、配置属性,自动装配“合理默认值”。
条件装配
核心注解:
@ConditionalOnClass@ConditionalOnMissingClass@ConditionalOnBean@ConditionalOnMissingBean@ConditionalOnProperty@ConditionalOnWebApplication@ConditionalOnResource@ConditionalOnExpression
示例:
|
注册自动配置
Spring Boot 3+/4 使用:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
内容:
com.example.autoconfig.AppDataSourceAutoConfiguration |
旧的 spring.factories 不再用于自动配置注册。
条件评估顺序
- 先判断 classpath
- 再判断 Bean 是否存在
- 再判断属性
- 最后才创建 Bean
常见问题:
- 自己定义了 Bean,但自动配置仍然生效:检查是否
@ConditionalOnMissingBean、Bean 名称/类型是否匹配 - 自动配置没生效:
--debug查看 conditions report - 自动配置顺序:用
@AutoConfigureBefore/@AutoConfigureAfter,不要依赖扫描顺序
覆盖自动配置
- 自己声明同类型 Bean
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)spring.autoconfigure.exclude=...- 用
@ConfigurationProperties调整参数,而不是复制整套配置
配置体系
配置来源优先级
从高到低(简化):
- 命令行参数
SPRING_APPLICATION_JSON- ServletConfig/ServletContext 参数
- JNDI
- Java System properties
- OS 环境变量
random.*- profile-specific 外部配置
- profile-specific 打包内配置
- 外部
application.* - 打包内
application.* @PropertySource- 默认属性
实践:
- 容器环境用环境变量覆盖
- 不要把生产密码写进 jar
- 用
spring.config.import引入外部配置 - 配置优先级要写进 runbook
application.yaml
spring: |
多 profile:
spring: |
application-prod.yaml 覆盖 application.yaml;多个 profile 时最后一个生效。
@ConfigurationProperties
|
启用:
|
优点:
- 类型安全
- 集中管理
- 支持校验
- 生成 metadata,IDE 可提示
注意:
- 构造器绑定需要
@ConstructorBinding或 record - 默认值用
@DefaultValue - 嵌套对象、List、Map 都支持
- 不要把
@ConfigurationProperties和@Component混用导致重复注册
配置校验
|
启动时失败比运行时报错好。
配置元数据
引入:
<dependency> |
IDE 会提示属性、类型、默认值。
配置加密
Spring Boot 不内置配置加密。常见:
- Vault
- AWS Secrets Manager
- Kubernetes Secret
- Jasypt(谨慎,密钥管理仍是问题)
- 云 KMS
不要把密钥和密文放在同一个仓库。
Bean 生命周期与依赖注入
生命周期
实例化 |
注入方式
- 构造器注入:推荐,依赖不可变、易测试
- Setter 注入:可选依赖
- 字段注入:不推荐,隐藏依赖、难测试
|
作用域
singleton:默认prototype:每次获取新实例request/session/application:Web 作用域- 自定义 scope
注意:
- singleton 注入 prototype 只会注入一次
- 需要每次新实例用
ObjectProvider - request scope 在异步线程中不可用
条件与 Profile
|
AOP 代理
@Transactional、@Async、@Cacheable、@Retryable 都依赖代理。
限制:
- 同类内部调用不经过代理,事务/异步/缓存失效
- private/final 方法不能被 CGLIB 代理
- 代理对象注入要按接口或类正确使用
- 自调用改用
TransactionTemplate、拆类或AopContext.currentProxy()
Web 开发
REST Controller
|
参数校验
public record CreateOrderRequest( |
@Valid触发 Bean Validation- 方法级
@Validated支持@RequestParam校验 - 自定义约束实现
ConstraintValidator - 校验失败返回 400,统一异常处理
统一异常处理
|
Spring Framework 6+/Boot 3+ 支持 ProblemDetail(RFC 9457),比自定义错误结构更标准。
过滤器与拦截器
Filter:Servlet 层,能改 request/response,适合认证、日志、CORS、压缩HandlerInterceptor:Spring MVC 层,能访问 Handler,适合权限、审计@ControllerAdvice:全局异常/数据绑定OncePerRequestFilter:避免重复执行
内容协商
produces/consumesAccept头- Jackson 3 默认
- 自定义
HttpMessageConverter
静态资源与上传
src/main/resources/staticspring.web.resources.*- multipart:
spring.servlet.multipart.max-file-size、max-request-size - 大文件用流式处理,不要全部读入内存
虚拟线程
spring.threads.virtual.enabled=true |
- Tomcat 请求处理可用虚拟线程
- 自动配置的 HTTP 客户端也会使用虚拟线程
- 阻塞代码受益明显
- 注意
synchronized导致的 pinning(JDK 24+ 有改进,但仍要评估) - 不要用虚拟线程替代连接池/限流
HTTP 客户端
RestClient
Spring 6.1+ 的同步客户端,替代 RestTemplate:
|
调用:
OrderResponse resp = restClient.get() |
HTTP Service Client
|
启用(Spring Framework 7 的 @ImportHttpServices 按组注册):
|
Spring Boot 4 提供自动配置和属性支持,也可以直接注入接口代理。
WebClient
- 响应式/非阻塞
- 适合高并发、流式
- 不要在 MVC 线程里
block() - 配置连接池、超时、重试、指标
超时与重试
- 连接超时、读超时都要设
- 重试只对幂等请求
- 配合熔断/限流(Resilience4j 等)
- 记录下游延迟和错误率
数据访问
JDBC
JdbcClient(Spring 6.1+)或JdbcTemplate- HikariCP 默认连接池
- 显式 SQL,性能可控
- 适合复杂查询/批量
List<Order> orders = jdbcClient.sql("select * from orders where user_id = :id") |
JPA / Hibernate
|
常见问题:
- N+1:用
@EntityGraph、fetch join、批量抓取 - 懒加载在事务外失败:DTO 投影或在事务内组装
- 大结果集:分页/流式
- 乐观锁:
@Version+ 重试 - 双向关系:维护一致性,避免无限递归(用 DTO)
Repository
public interface OrderRepository extends JpaRepository<Order, Long> { |
- 方法名查询适合简单条件
- 复杂查询用
@Query/Specification/Querydsl - 分页用
Pageable - 投影用 interface/record DTO,避免实体泄漏
迁移
- Flyway/Liquibase
- 迁移脚本版本化,禁止手工改生产库
- 大表变更评估锁和回滚
- 迁移与应用发布解耦
连接池
spring.datasource.hikari.maximum-pool-size=20 |
- 池大小按数据库容量和并发,不是越大越好
- 连接泄漏会耗尽池
- 监控 active/idle/pending
事务与并发
@Transactional
|
关键属性:
propagation:REQUIRED、REQUIRES_NEW、NESTED 等isolation:READ_COMMITTED、REPEATABLE_READ 等readOnlytimeoutrollbackFor/noRollbackFor
默认:
- 运行时异常回滚
- 受检异常不回滚(需显式配置)
- 只对 public 方法生效(代理限制)
常见坑
- 同类自调用,事务不生效
- 异常被 catch 后没抛出,事务不回滚
- 事务中调用远程服务,长事务占用连接
@Transactional放在 private/final 方法- 多数据源时事务管理器不匹配
- 异步线程不继承事务
事务模板
需要细粒度控制时:
transactionTemplate.execute(status -> { |
异步
|
- 自定义
TaskExecutor,不要用默认无界队列 - 处理异常(
AsyncUncaughtExceptionHandler) - 事务上下文不传播
- 优雅停机要等待任务
调度
|
- 多实例会重复执行,需要分布式锁/调度平台
- 任务要幂等
- 长任务不要阻塞调度线程池
- 配置
spring.task.scheduling.pool.size
安全
SecurityFilterChain
|
OAuth2/JWT
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://issuer.example.com |
- 校验 issuer、audience、过期时间
- 自定义
JwtAuthenticationConverter映射权限 - 不要自己解析 JWT 而不校验签名
- 时钟偏差要留余量
方法安全
|
需要 @EnableMethodSecurity。
CORS/CSRF
- 前后端分离通常关闭 CSRF(用 token 认证)
- 使用 Cookie 会话时要防 CSRF
- CORS 明确 allowedOrigins,不要
*+ credentials
密码
BCryptPasswordEncoder/Argon2PasswordEncoder- 不要用 MD5/SHA1
- 不要在日志打印密码/token
Actuator 与可观测性
依赖
<dependency> |
暴露端点
management.endpoints.web.exposure.include=health,info,metrics,prometheus |
生产不要暴露 env、heapdump、threaddump 给公网。
健康检查与探针
management.endpoint.health.probes.enabled=true |
/actuator/health/liveness/actuator/health/readiness- Kubernetes 用 liveness/readiness/startup probes
- readiness 要包含数据库/下游关键依赖
- liveness 不要包含外部依赖,避免雪崩重启
指标
- Micrometer
MeterRegistry @Timed、@Counted、@Observed- HTTP server/client 指标
- JVM、Hikari、Tomcat、Kafka 等
- Prometheus endpoint 或 OTLP
追踪
- Micrometer Tracing + OpenTelemetry
spring-boot-starter-opentelemetry- W3C trace context
- 日志关联 traceId/spanId
- 采样率按流量调整
日志
- Logback 默认
- 结构化日志(JSON)
- 请求 ID/MDC
- 不要记录敏感信息
logging.console.enabled=false可关闭控制台
测试
@SpringBootTest
|
- 启动完整上下文,慢
- 尽量复用上下文缓存
- 不要在每个测试改配置导致新上下文
切片测试
@WebMvcTest:Controller 层@DataJpaTest:JPA + 内存库/Testcontainers@JsonTest:JSON 序列化@RestClientTest:HTTP 客户端@JdbcTest:JDBC
切片测试快,但只覆盖一层。
Mock
- Spring Boot 3.4+ 使用
@MockitoBean替代@MockBean @MockitoSpyBean替代@SpyBean- 不要 mock 掉被测对象
Testcontainers
|
- 真实数据库/中间件
@ServiceConnection自动注入连接信息- CI 需要 Docker
- 容器启动慢,注意复用
测试配置
application-test.yaml@DynamicPropertySource@TestPropertySource- 随机端口
- 不要依赖生产数据
AOT 与原生镜像
AOT
Spring Boot 支持 AOT 处理:
- 提前生成 Bean 定义、代理、反射提示
- 缩短启动时间
- 为 GraalVM native image 做准备
mvn -Pnative native:compile |
或 Buildpacks:
mvn spring-boot:build-image -Pnative |
原生镜像限制
- 反射、动态代理、JNI 需要 hints
- 类路径在构建时固定
- 启动快、内存低,但构建慢
- 不适合频繁动态加载类的应用
- 测试要覆盖 native 模式
注册反射:
或实现 RuntimeHintsRegistrar。
打包与部署
可执行 Jar
mvn clean package |
Spring Boot Maven Plugin 生成可执行 jar。
分层 Jar
用于 Docker 缓存:
<layers> |
java -Djarmode=tools -jar app.jar extract --layers --destination extracted |
Buildpacks
mvn spring-boot:build-image |
- 不需要写 Dockerfile
- 自动分层、JVM 优化
- 可定制 builder
Dockerfile
FROM eclipse-temurin:21-jre |
Kubernetes
livenessProbe: |
优雅停机:
server.shutdown=graceful |
- 先摘流量,再停机
- 处理 SIGTERM
- 等待进行中的请求完成
性能与调优
Tomcat
server.tomcat.threads.max=200 |
- 线程数不是越大越好
- 阻塞 IO 多时增加线程/虚拟线程
- 连接数受文件句柄和内存限制
- 监控 busy threads、queue、latency
虚拟线程
- 适合大量阻塞 IO
- 减少线程池大小
- 注意 pinning(synchronized/native)
- 数据库连接池仍是瓶颈
- 压测验证
连接池
- Hikari 最大连接数 = 数据库容量 / 实例数
- 连接等待时间要短
- 监控 active/idle/pending/timeout
缓存
@Cacheable、Caffeine、Redis- 缓存穿透/击穿/雪崩
- TTL、容量、一致性
- 不要缓存敏感数据
JVM
- 容器感知
MaxRAMPercentage - G1/ZGC 按延迟要求
- GC 日志/JFR
- 堆外内存(Netty)要计入容器 limit
常见坑
@Transactional自调用失效@Async自调用失效- 字段注入导致循环依赖/难测试
@ConfigurationProperties没加@EnableConfigurationProperties/@ConfigurationPropertiesScan- profile 配置覆盖顺序搞错
- 自动配置排除后忘了补 Bean
- N+1 查询
- 懒加载在事务外
- 大事务占用连接
- 连接池太小/太大
- Actuator 暴露敏感端点
- JWT 不校验签名/issuer
- CORS 配置过宽
- Jackson 2→3 包名/行为变化
- native image 反射缺失
- 测试上下文缓存被破坏
- 依赖冲突(Jakarta/Servlet/Jackson)
- 用
spring.jpa.hibernate.ddl-auto=update上生产 - 生产开
devtools - 日志打印敏感信息
企业实践
分层与边界
- Controller 只做协议适配
- Service 承载业务和事务
- Repository 只做数据访问
- DTO 与实体分离
- 不要让 JPA 实体直接暴露到 API
配置管理
- 12-factor:配置与环境分离
- 生产配置走 ConfigMap/Secret/Vault
- 配置变更可审计、可回滚
- 启动时校验配置
- 敏感配置不落盘
可观测性
- 指标、日志、追踪统一
- 关键接口 SLO
- 错误率/延迟/饱和度
- 启动/关闭事件
- 依赖健康
发布
- 灰度/金丝雀
- 数据库迁移向前兼容
- 健康探针
- 优雅停机
- 回滚方案
安全
- 依赖漏洞扫描
- 最小权限
- 密钥轮换
- 安全响应头
- 输入校验
- 审计日志
测试
- 单元 + 切片 + 集成 + 契约
- Testcontainers
- native 测试
- 性能基线
附录
常用注解
@SpringBootApplication@Configuration@Bean@ConfigurationProperties@EnableConfigurationProperties@ConditionalOn*@Transactional@Async@Scheduled@RestController@ControllerAdvice@Validated@PreAuthorize@Observed/@Timed
常用配置
spring.application.name=app |