Spring Boot · 第 06 章
06 · 现代栈与前沿
前五章是自 Spring 3 起稳定的核心机制——这一章是截至 2026-06 仍在动的部分:Boot 4.0 的破坏性变更、虚拟线程与瓶颈转移、三条快启动路线、Spring AI 的落地位置,以及与 Quarkus/Micronaut 的选型轴。
本章你将建立的 schema
- Boot 4.0(2025-11 GA,4.0.6 / 2026-04)破坏性变更清单:Jakarta EE 11、Jackson 3、Undertow 移除、Java 17 起步
- 虚拟线程开启后瓶颈从 Tomcat 线程池(默认 200)转移到 HikariCP 连接池(默认 10)——两者缺一不可
- 三条快启动路线(CDS / GraalVM Native / Java 25 AOT cache)各买什么、各有什么约束;Spring AI 是同一套自动配置模型的延伸;Spring vs Quarkus/Micronaut 的决策轴
6.1Boot 4.0 改了什么(2025-11 GA,4.0.6 / 2026-04)
Boot 4.0 于 2025-11 发布 GA,当前最新稳定版为 4.0.6(2026-04)。底层依赖升至 Spring Framework 7 + Jakarta EE 11——所有包名从 javax.* 改为 jakarta.*,这是自 Boot 3.0 以来的第二次命名空间迁移。
主要破坏性变更:Jackson 3 成为默认序列化库(Jackson 2 的 DeserializationFeature 等 API 有不兼容变动);内嵌服务器中 Undertow 已移除,选项只剩 Tomcat(默认)和 Jetty,响应式栈仍用 Netty;Spock 测试框架支持移除;Java 17 是最低运行时(构建链路用到 Java 25 特性)。
并行维护的 3.5.x 仍在接收补丁(长期维护分支);4.1.0-RC1(2026-04) 已发布,GA 在即,带入 Java 25 AOT cache 支持(见 6.4 节)。
| 维度 | Boot 3.x 做法 | Boot 4.0 要求 |
|---|---|---|
| 包命名空间 | javax.*(Boot 3 已迁到 jakarta,但部分旧依赖混用) |
全量 jakarta.*,Jakarta EE 11 API |
| JSON 库 | Jackson 2.x | Jackson 3.x 默认;API 有不兼容变动(DeserializationFeature 等) |
| 内嵌服务器 | Tomcat / Jetty / Undertow | Undertow 已移除;仅 Tomcat / Jetty(响应式用 Netty) |
| HTTP 客户端 | RestTemplate(维护模式) |
推荐 RestClient(同步)/ WebClient(响应式) |
| Java 基线 | Java 17(Boot 3.0)/ Java 21(Boot 3.2) | 运行时 Java 17 起步,构建 Java 25 |
| 自动配置注册 | META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(2.7 起) |
同上,未变 |
把 Boot 3.x 项目升到 4.0 时,javax.* 依赖(包括老版本 Hibernate、Servlet 容器的传递依赖)若未随之升级,编译期会报大量 package javax.* does not exist。先用 ./mvnw dependency:tree | grep javax 扫出残留,再逐一升版本或换替代库。
6.2可观测性:Micrometer 取代 Sleuth(3.0 起稳定)
可观测性的统一入口是 Micrometer Observation API——它把 metrics 和 traces 收归同一个埋点模型。
Spring Cloud Sleuth 已于 2022-12 进入 attic(归档),未移植到 Boot 3+。Boot 3.0 起,分布式追踪由 Micrometer Tracing 承接,底层可对接 Zipkin、Jaeger、OTLP 等后端。Trace context 默认遵循 W3C Trace Context 规范,traceId 为 128-bit。
埋点方式:直接使用 ObservationRegistry 或注解 @Observed(Boot 3.2+ 自动为 Spring MVC 控制器、Spring Data 方法、Kafka listener 等织入 observation)。Actuator 的 /actuator/metrics 和 /actuator/httptrace(Boot 4.0 已更名为 /actuator/httpexchanges)继续可用。
代码里 import 了 org.springframework.cloud.sleuth.* 或 pom 里留有 spring-cloud-starter-sleuth——这些依赖在 Boot 3+ 不存在对应版本,会导致依赖解析失败。替换为 spring-boot-starter-actuator + io.micrometer:micrometer-tracing-bridge-brave(或 otel)。
6.3虚拟线程(3.2 起)与瓶颈转移
开启虚拟线程后,服务器线程数不再是吞吐上限——新瓶颈是 HikariCP 的连接池大小。
Java 21 正式发布虚拟线程(Project Loom GA)。Boot 3.2(2023-11)随即引入一行配置支持:
spring:
threads:
virtual:
enabled: true # Java 21+;Tomcat 改用虚拟线程派发请求
效果:Tomcat 的请求派发线程由平台线程换成虚拟线程。虚拟线程是 JVM 层面轻量级调度单元,阻塞 I/O 时不占用 OS 线程,因此 server.tomcat.threads.max(默认 200)这个上限几乎不再成为瓶颈——系统可以并发数千个请求,每个请求阻塞等待数据库时不耗 OS 线程。
但瓶颈立刻转移到 HikariCP。 HikariCP 默认连接池大小为 10。当 5000 个虚拟线程同时发起数据库调用时,999 个在 HikariCP 的等待队列里排队,超时抛出 Connection is not available, request timed out after 30000ms。
正确做法:开虚拟线程的同时按预期并发量调大连接池。
spring:
threads:
virtual:
enabled: true
datasource:
hikari:
maximum-pool-size: 50 # 按实际 DB 并发承载量设置,非越大越好
数据库服务器的连接数有上限(PostgreSQL 默认 100,MySQL 默认 151)。连接池超出 DB 服务器上限只会把排队从 HikariCP 挪到 DB 端,问题不减反增。调参前先确认 DB 侧的 max_connections。
一个服务已经开了 spring.threads.virtual.enabled=true,但没有数据库操作——只做纯 CPU 计算或调用外部 HTTP 接口。此时 HikariCP 瓶颈还成立吗?
展开答案(先停 10 秒)
不成立。HikariCP 瓶颈只在有 JDBC 阻塞调用时才出现。纯 HTTP 出站(WebClient / RestClient)由各自的连接池管理;纯 CPU 计算的瓶颈是 CPU 核数——虚拟线程对 CPU 密集型服务几乎没有收益,反而可能因调度开销轻微下降。
6.4三条快启动路线
降低启动时间与内存占用有三条正交路线,各有不同的成本结构(2026-06 现状):
CDS(Class Data Sharing,Boot 3.3+)
JVM 的 CDS 把已解析的类元数据持久化到共享归档文件,下次启动直接 mmap 进内存,跳过类加载与字节码校验。Boot 3.3(2024-05)通过 spring-boot:process-aot + JVM flag 自动化 CDS 归档生成。启动时间缩短约 20–40%,无"闭世界"约束,对反射友好。代价:归档文件与应用版本绑定,每次升级 jar 需重新生成;容器镜像须固定 JVM 版本。
GraalVM 原生镜像(stable since Boot 3.0,2022-11)
GraalVM 的 native-image 工具在构建期做闭世界分析(closed-world analysis):把可达代码、反射元数据、资源清单全部静态分析并编译进单一可执行文件。结果:启动 50–100ms(JVM 同等应用 2–8s),RSS 减半。
代价是"闭世界"约束:运行时反射需要 AOT hints。Boot 的 RuntimeHintsRegistrar 和 @RegisterReflectionForBinding 用于声明反射需求;Boot 自身及主流 starter 已内置 hints,但自定义代码(尤其是动态类加载、第三方库的非注册反射)需手动补充,否则 native 镜像运行时抛 MissingReflectionDataException。验证手段:native:test goal 在 native 镜像内跑测试套件。
原生镜像并不改变自动配置的逻辑——@Conditional 仍然决定哪些 @Bean 注册(见第 3 章 6.4 节)。区别在于:JVM 模式下条件在运行时求值,原生镜像模式下条件在构建期由 AOT 处理器求值并"烘焙"进镜像,运行时不再重新计算。这意味着 @ConditionalOnProperty 若依赖运行时环境变量,需要确认 AOT 处理器能正确处理它。
Java 25 AOT cache(Boot 4.1,2026-04 RC1)
Java 25 引入应用层 AOT 缓存(JEP 483),把 JIT 编译后的代码缓存到磁盘,下次启动直接加载已编译代码,跳过 JIT 热身阶段。Boot 4.1.0-RC1(2026-04)已对接此特性。与 GraalVM native 相比,无闭世界约束,仍是标准 JVM,但热身阶段缩短显著。GA 时间待定。
| 路线 | 启动时间 | RSS | 主要约束 | Boot 版本 |
|---|---|---|---|---|
| CDS 类数据共享 | 快 20–40% | 改善有限 | 归档与 jar 版本绑定,升级须重建 | 3.3+(2024-05) |
| GraalVM 原生镜像 | 50–100ms | 减半 | 闭世界;反射需 AOT hints;构建慢 | 3.0 stable(2022-11) |
| Java 25 AOT cache | 跳过 JIT 热身 | 同 JVM | 需要 Java 25;Boot 4.1 尚未 GA | 4.1.0-RC1(2026-04) |
6.5Spring AI(1.0 GA 2025-05,1.1 GA 2025-11)
Spring AI 是同一套自动配置模型的延伸——starter + @Conditional + 自动装配的 ChatClient,没有引入新的容器机制。
Spring AI 1.0 于 2025-05 发布 GA;1.1 于 2025-11 发布 GA,新增 MCP(Model Context Protocol)集成。核心 API:
ChatClient(1.0 重命名自ChatModel):统一跨 20+ 模型提供商(OpenAI、Anthropic、Google Gemini、Ollama 等)的对话接口,通过spring-ai-{provider}-spring-boot-starter引入对应实现EmbeddingModel:文本向量化;VectorStore:向量相似度检索(对接 pgvector、Chroma、Redis、Qdrant 等)- 工具/函数调用:通过
@Tool注解将 Spring Bean 方法暴露给模型 QuestionAnswerAdvisor(RAG 检索增强生成):内置 Advisor 链,自动把向量检索结果注入 prompt context- MCP 集成(1.1):
spring-ai-mcp-spring-boot-starter,把 Spring 应用作为 MCP Server 或 MCP Client 暴露/消费工具
它的自动配置完全遵循第 3 章的机制:ChatClientAutoConfiguration 带 @ConditionalOnClass(ChatModel.class),只在对应 provider starter 在 classpath 上时激活;用户自定义 ChatClient.Builder Bean 会触发 @ConditionalOnMissingBean back-off(见第 3 章 3.5 节)。
如果对 MCP 协议的 wire-level 机制感兴趣,可参考同系列教程:MCP 协议深挖;RAG 的向量检索与召回机制参见:RAG 系统设计。
<!-- Spring AI 1.1 BOM (GA 2025-11) -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- OpenAI provider -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- MCP integration (1.1+) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-spring-boot-starter</artifactId>
</dependency>
</dependencies>
6.6选型轴:Spring vs Quarkus / Micronaut
三个框架在 DI 机制上的根本差异(2026-06):
| 维度 | Spring Boot | Quarkus | Micronaut |
|---|---|---|---|
| DI 机制 | 运行时反射(+ AOT 补足) | 构建期 ArC(CDI 子集) | 编译期注解处理器生成代码 |
| 原生镜像友好度 | 中(需 AOT hints) | 高(天生为 native 设计) | 高(无运行时反射) |
| 启动速度(JVM 模式) | 中(2–8s) | 快 | 快 |
| 生态广度 | 最广(starter 生态、Spring Data/Security/AI 等) | 中 | 中 |
| 注解模型兼容性 | Spring 全家桶原生 | 兼容部分 Spring 注解(Quarkus Spring extensions) | 部分兼容 Spring 注解 |
Spring 的 AOT + 原生镜像路线(Boot 3.0+)缩小了启动差距,但构建时间与闭世界调试成本仍高于 Quarkus/Micronaut。决策轴:
- 已有大量 Spring 代码或生态依赖(Spring Data、Spring Security、Spring AI)→ Boot + CDS 或 Boot + Native
- 全新项目、Serverless / FaaS、对启动时间极敏感、团队愿意接受编译期约束 → Quarkus 或 Micronaut
- 需要 GraalVM native 但又不想处理反射 hints → Quarkus(其 ArC 容器天生无运行时反射)
Spring 的运行时反射优势体现在:ConfigurationClassPostProcessor 在 invokeBeanFactoryPostProcessors 阶段动态扫描 @Configuration(见第 2 章 2.2 节);Quarkus 的等价扫描发生在 Maven/Gradle 构建阶段,产物已确定——失去了运行时动态注册 BeanDefinition 的能力,换来启动速度与 native 友好性。
§本章 self-check
先合上教程,把答案写下来再展开对照。
- Spring Cloud Sleuth 被什么取代?归档时间是哪年哪月?为什么 Boot 3+ 项目不能再引入 Sleuth?
- 开启
spring.threads.virtual.enabled=true后,系统的新瓶颈在哪里?如果不处理会出现什么报错? - GraalVM 原生镜像的"闭世界"约束意味着什么?开发时需要额外做什么才能让自定义反射代码在 native 镜像中正确运行?
答案(先做完再展开)
- Sleuth 被 Micrometer Tracing 取代。Sleuth 于 2022-12 进入 attic(归档)并明确不移植到 Boot 3+。Boot 3+ 项目引入
spring-cloud-starter-sleuth会因无对应版本而依赖解析失败,且概念层面 Micrometer Observation API 已完全覆盖其功能。 - 新瓶颈是 HikariCP 连接池(默认
maximum-pool-size=10)。当虚拟线程数远超连接池大小时,JDBC 调用在 HikariCP 等待队列超时,抛出Connection is not available, request timed out after 30000ms。解决方法:按 DB 承载量同步调大spring.datasource.hikari.maximum-pool-size。 - "闭世界"意味着
native-image构建器在构建期静态分析所有可达类型——运行时无法加载构建期未分析到的类、也无法使用未注册的反射调用。开发时需要通过RuntimeHintsRegistrar或@RegisterReflectionForBinding显式声明反射需求,或使用-agentlib:native-image-agent运行时代理自动收集 hints;并通过mvn native:test在 native 镜像内验证测试通过。
虚拟线程 + 调大连接池 vs GraalVM 原生镜像:决策轴是什么?
一个 IO 密集型服务(大量数据库查询 + 外部 HTTP 调用),需要在高并发下保持低延迟,同时对冷启动时间有要求(容器扩容必须在 2s 内就绪)。给定以下两个方案:① Boot 4.0 + 虚拟线程 + HikariCP 调大到 50;② Boot 3.x + GraalVM 原生镜像 + 平台线程。决策轴是什么?哪些信息影响最终选择?
提示(卡住再展开)
核心决策轴不是"哪个更快",而是约束侧:① 团队是否有能力维护 AOT hints(第三方库的反射调用是否都有现成 hints?);② DB 侧 max_connections 能允许多大的连接池,这决定虚拟线程方案的上限;③ 2s 冷启动要求——JVM + CDS 约 600ms~1.5s,GraalVM native 约 50–100ms,虚拟线程 JVM 模式能否满足取决于 JVM 启动基线。另一个维度:是否需要 GraalVM native 的内存优势(Serverless 按内存计费时 native 有明显成本收益)。先量再选,不要凭感觉。