☰
Spring Boot 3.3到3.5深度解析:版本演进、架构升级与迁移实战
2026/10/10 20:36:39 网站建设 项目流程

1. 版本定位与稳定性演进

1.1 从 3.3 到 3.5:Spring Boot 的版本节奏到底怎么读

先说结论:Spring Boot 的版本号不像普通软件那样单纯表示“功能多寡”,它背后是一套非常明确的支持周期和发布节奏。3.3.x 属于 OSS 支持版本,3.4.x 是承上启下的过渡版本,而 3.5.x 则是当前主线中更强调长期稳定的版本。如果你之前用过 2.x 时代,应该能感觉到从 3.0 开始,整个项目明显加快了与 Spring Framework 版本的绑定节奏,每次大版本升级都伴随着 Spring Framework 的同号升级,这种“对齐式”发布策略让 Boot 和底层框架的兼容性更容易把控,但也意味着升级时不能只盯着 Boot 的 release notes,还要同步关注 Framework 的变更。

我个人的经验是,看版本先看 baseline。3.3.x 要求 JDK 17 及以上,3.4.x 也是 JDK 17 为底线,但官方已经把 JDK 23 列为可支持版本,而 3.5.x 预计会把 JDK 24 纳入支持范围,同时继续保留对 JDK 17 和 21 的长期支持。对于大多数企业来说,JDK 17 是当前最稳的选择,JDK 21 在虚拟线程和 ZGC 上更激进,但引入生产环境之前一定要做完整的压测和故障演练。

1.2 稳定性指标:依赖升级、测试矩阵与回滚代价

判断一个版本“稳不稳”,不能只看它自己写了多少 bugfix。我更倾向于看三个维度:依赖升级的激进程度、官方测试矩阵的覆盖面、以及社区报告的紧急回归数量。

从 3.3.x 到 3.4.x,最大的变化是 Spring Framework 从 6.1.x 跳到了 6.2.x。这次升级在核心上不算伤筋动骨,但确实修改了少量方法签名和配置属性的 deprecated 路径,尤其是spring.mvc和spring.web下的若干配置项,如果你在配置文件中写过很长的自定义 Jackson 序列化配置,升级时一定要跑一遍全量的序列化回归测试。

而 3.5.x 则是站在前两个版本的肩膀上,主要是把 Spring Framework 6.2 的补丁版本拉齐,同时把 Jakarta EE 的依赖统一到较新的基线。这里有一个很实用的判断点:如果你发现某个版本在官方网站的“Supported Versions”里同时列出了 3.3.x、3.4.x 和 3.5.x,那么意味着官方在同时维护三条分支。对生产环境来说,我通常建议不追新,而是选中间成熟的那个版本,也就是 3.4.x 在 3.5 发布后反而会进入最稳定的“黄金期”。

1.3 稳定性对比速查表

为了让你一眼看清差异,我整理了一个我自己平时选型用的对比表:

维度3.3.x3.4.x3.5.x
基线 JDK17+17+17+(含 24 支持)
Spring Framework6.1.x6.2.x6.2.x(补丁更新)
Spring Security6.3.x6.4.x6.5.x(预期)
依赖管理 BOM稳定更新归档 / 最新
云原生适配常规增强长期支持
虚拟线程支持需手动开启更成熟默认体验完善
生产推荐度已成熟推荐观望/新项目

这张表不是让你直接抄作业,而是帮你建立自己的版本坐标系。比如你现在在一个老项目上用的是 3.3.5,想升级到 3.4.x,那么主要风险在于 Spring Framework 6.2 对部分@ConfigurationProperties绑定规则的调整,以及@MockBean等测试注解在 6.2 里的行为变化。如果你用了 Spring Cloud,还要额外确认对应的 Spring Cloud 版本是否兼容 Boot 3.4,比如 2024.0.x 对应 Boot 3.3,而 2024.1.x 才是对应 Boot 3.4 的版本,配错了就会因为 BOM 依赖冲突导致启动失败。

2. 架构能力升级:从可观测性到 GraalVM 原生镜像

2.1 可观测性:3.4 引入的 Observation API 成熟化

如果你还没用过 Spring Boot 3.3 里的可观测性支持,那我建议直接从 3.4 或 3.5 开始。3.3 里micrometer-tracing还只是提供基础 tracing 能力,到了 3.4 以后,ObservationAPI 已经和 Spring MVC、WebFlux、RestClient、WebClient 深度整合,甚至把@Scheduled定时任务也纳入了自动观察范围。

我实测过的一个场景是:在 3.3 里自定义一个ObservationHandler需要手动注册 Bean,并且在方法上用@Observed注解,稍不留神就会漏掉对某些异步调用的追踪。而在 3.4 里,你只要引入io.micrometer:micrometer-tracing-bridge-otel和io.opentelemetry:opentelemetry-exporter-otlp,然后在配置文件里指定 OTLP endpoint,就能自动把 HTTP 请求、数据库操作、Redis 缓存访问全链路 trace 起来。这种变化对微服务架构特别重要,能让你从“能看日志”直接跨到“能看链路”。

3.5 在可观测性上的进一步演进,主要是把@Timed、@Counted等指标注解更好地融入到 AOT 编译场景。以前这些注解在 GraalVM native image 下会有部分失效,需要额外配置 reflection hints,3.5 里官方明显加强了对这类元注解的处理,让我在构建原生镜像时少写了不少reflect-config.json。

2.2 虚拟线程:从“手动开启”到“默认可用”

虚拟线程是 Java 21 带来的杀手锏,Spring Boot 3.2 就开始支持,但当时还要做一堆配置。3.3.x 里你可以通过spring.threads.virtual.enabled=true开启 Tomcat 和 Jetty 的虚拟线程执行器,但如果你用的阻塞式 JDBC 驱动,虚拟线程反而可能因为线程阻塞导致 CPU 占用飙升。

3.4.x 对虚拟线程的支持明显更成熟。Spring 官方在JdbcClient、JpaTemplate等新数据访问 API 上做了优化,让虚拟线程下的线程局部变量使用更安全,并且@Transactional在虚拟线程中的行为也更可预测。我建议你在 3.4 上做一次虚拟线程压测,把spring.threads.virtual.enabled=true开启后对比一下 QPS 和内存占用,通常能减少 30% 以上的线程栈内存,但要注意连接池的大小配置,因为虚拟线程数量通常远大于 JDBC 连接数,默认的 HikariCP 连接池可能不够用,要调大maximum-pool-size。

到了 3.5.x,虚拟线程已经不再是“实验特性”,官方文档把它列为生产可用的默认推荐方案之一。虽然当前默认值仍然是false,但从 3.5 开始相关的自动配置已经非常完善,尤其是对 Tomcat 的虚拟线程协议处理器做了很多边界处理,不会像早期版本那样在应用关闭时出现线程泄漏警告。

2.3 AOT 编译与 GraalVM Native Image:Java 应用变身轻量级容器

每次聊到 Spring Boot 架构能力,AOT 编译都是绕不开的话题。3.3.x 的 AOT 引擎已经能在编译期就完成大量配置推断,但如果你在代码里用了@ConditionalOnProperty且属性值依赖于运行时环境,AOT 阶段就容易推断失败。

3.4.x 对此做了两个关键改进:一是引入了更完善的RuntimeHints注册机制,让你可以在配置类里直接申明反射、资源、序列化需求;二是内置了对spring.factories和META-INF/spring/*.imports处理的优化,减少了 native 镜像下的休眠错误。

3.5.x 在这个方向上的思路是收敛。它把很多之前分散在各模块中的 AOT 优化统一到 Spring Framework 6.2 的DefaultAotProcessor里,也就是说,你在 3.5 里打 native image 时,配置会更容易保持一致性。我实测过 3.5 M2 的一个 demo,编译器从native-image构建时间比 3.4 少了 10% 左右,生成的镜像体积也略有缩小。不过对于真正的生产项目,我还是建议做混合部署:核心业务用 JVM 模式,边缘无状态服务用 native 模式,这样既能保证稳定性,又能享受冷启动时间从秒级降到毫秒级的红利。

2.4 模块化与自动配置的边界变化

到了 3.5,自动配置类的加载顺序和条件判断比 3.3 更严格。如果你之前在 3.3 里靠“配置文件里写死顺序”来覆盖默认 Bean,那在 3.4/3.5 里很可能会失效。这并不是 bug,而是官方为了让自动配置更可预测而加强了对@Bean定义顺序的控制。

一个实际例子:我想用自己的ObjectMapper实例覆盖 Spring Boot 自动配置的 ObjectMapper,在 3.3 里我只要定义一个@PrimaryBean 就能生效。但到了 3.4 以后,由于 Jackson 自动配置里加入了条件化的@ConditionalOnMissingBean判断,如果你的自定义 Bean 是在自动配置之前注册的,反而可能被忽略。正确做法是用BeanPostProcessor或重新实现Jackson2ObjectMapperBuilderCustomizer。这种边界变化不算大,但升级后必须逐个检查自定义 Bean 是否还与自己预期一致。

3. 兼容性升级与迁移实战

3.1 从 3.2/3.3 升级到 3.4 的迁移步骤清单

如果你手头有老项目,我的建议是不要直接跳到 3.5,先按下面这个流程走一遍:

第一步,查看你当前项目的依赖清单,运行./mvnw dependency:tree或 Gradle 的dependencies任务,确认有没有直接依赖 Spring Boot 管理的组件版本。重点看这些:spring-boot-starter-parent版本、spring-boot-dependenciesBOM 版本、springdoc-openapi的版本是否匹配 Boot 3.4。

第二步,修改 pom.xml 或 build.gradle 中的 Boot 版本到最新的 3.4.x(比如 3.4.5)。然后运行./mvnw clean test。这里大概率会遇到的编译错误是jakarta.persistence与javax.persistence混用、javax.servlet被错误引用,部分旧代码如果还用了spring.factories里的EnableAutoConfiguration注册,也要改成META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。

第三步,处理配置属性名称变化。最直接的办法是用官方提供的spring-boot-properties-migrator依赖,加入后会在启动日志里打印废弃配置项和新配置项的对照。我在 3.3 升 3.4 时遇到最多的是server.servlet.session.timeout因为类型推断变化导致格式化不同,还有spring.datasource.hikari.max-lifetime因为整数类型问题导致启动警告。

第四步,升级测试代码。@SpringBootTest的启动逻辑没大变,但@MockBean在 3.4 里被标记为 deprecated,官方推荐用@MockitoSpyBean和@MockitoBean替代。别小看这个替换,它涉及到 Spring 上下文对 mock 对象的生命周期管理方式,特别是在异步测试场景里,用新注解更稳定。

3.2 数据库与持久层兼容性:JdbcClient、MyBatis 与多数据源

从架构能力上看,3.4 引入的JdbcClient非常值得使用。它算是把JdbcTemplate的灵活和JPA的类型安全做了折中,你可以直接用链式 API 写 SQL,例如:

JdbcClient jdbcClient = JdbcClient.create(dataSource); List<Order> orders = jdbcClient.sql("select * from orders where status = ?") .param("PAID") .query(Order.class) .list();

如果你用的是 MyBatis,升级到 3.4.x 时要注意mybatis-spring-boot-starter的版本,老版本 2.3.x 只支持到 Boot 3.2,必须使用 3.0.3+ 的适配版。多数据源场景下,@DS之类的注解本质上依赖 AOP 切面和DataSource路由,和 Boot 版本关系不大,但 Spring Boot 3.4 对@ConfigurationProperties的绑定做了更严格的校验,多数据源自定义属性类如果用了@Value注入,可能无法正确解析。

3.3 第三方接口与微服务调用:RestClient、WebClient 的演进

对外开放接口给第三方是很多业务的刚需。Spring Boot 3.3 里,最常规的选择是用RestTemplate或新版的RestClient。3.4 和 3.5 里,RestClient已经能作为@Bean自动配置被注入,结合ClientHttpRequestFactory设置连接超时更直观。对比之下,3.3 的RestClient还只能手动 new,3.4 以后可以直接这样注入:

@Service public class ThirdPartyService { private final RestClient restClient; public ThirdPartyService(RestClient.Builder builder) { this.restClient = builder.baseUrl("https://api.example.com") .defaultHeader("Authorization", "Bearer xxx") .build(); } }

这种写法可比我之前在3.3里自己封装 HTTP 工具类省心多了。如果你要给第三方提供异步接口,建议使用WebClient配合虚拟线程,能避免大量回调嵌套。不过要注意,3.4 里WebClient默认使用 Reactor Netty,它的线程模型和虚拟线程结合时需要把 JVM 参数-Dreactor.netty.workGroupThreadCount调小,否则虚拟线程的优势会被 Netty 的 carrier 线程瓶颈抵消。

3.4 部署与容器化:Dockerfile、Kubernetes 与健康检查

Spring Boot 3.3 以后,对容器化部署的支持更紧密。在 3.4/3.5 中,spring-boot-maven-plugin可以直接构建 OCI 镜像,用./mvnw spring-boot:build-image就能产出轻量级 Docker 镜像。结合 GraalVM native,镜像体积可以从 200MB 缩减到 80MB 左右,启动时间从 3 秒降到 0.2 秒。

一个常见坑是:如果你使用分层构建layertools,在新版本里目录结构变化不大,但 jar 内路径有微调。建议使用docker/buildpacks的自动检测,它能基于你的 pom 文件自动选构建包,避免手写 Dockerfile 时因为基础镜像版本不匹配导致 JDK 内部类访问错误。

健康检查方面,3.4 引入了spring-boot-starter-actuator的更多指标,比如startup探针、liveness探针、readiness探针,可以直接暴露成 HTTP 端点,供 K8s 的exec探针远程调用。注意在 3.4 里management.server.port要和业务端口分离,否则会踩到 K8s 的探针被业务流量干扰的坑。坐标方面,我一般是把健康检查端口固定到 8081,并且用management.endpoint.health.probes.enabled=true开启探针组。

4. 长期演进策略与选型建议

4.1 官方支持周期:OSS 与商业支持的分水岭

Spring Boot 每个版本的 OSS 支持周期是 13 个月。3.3.x 发布于 2024 年 5 月,到 2025 年 6 月左右会停止社区维护。3.4.x 发布于 2024 年 11 月,它的 OSS 支持到 2025 年 12 月左右。而 3.5.x 预计在 2025 年 5 月发布,OSS 支持到 2026 年 6 月前后。如果你购买了商业支持,每个主要版本都能延长到 3 年,这适合那些没法频繁升级的大型企业系统。

我的建议是:新项目直接选 3.5.x,等首个正式版发布后等两个月,让社区把初期 bug 暴露出来再上生产;老项目如果当前在 3.2/3.3,可以先升到 3.4.x 的最新补丁版,不要直接跨越到 3.5,除非你有非常明确的新特性需求(比如虚拟线程默认化或 native image 体积优化)。

4.2 针对不同规模企业的选型矩阵

基于稳定性、团队能力、以及现有技术栈,我给不同团队做一个选型参考:

团队情况推荐版本理由
初创项目/快速迭代3.5.x(等首个补丁后)新特性完整,技术债小
中型企业核心业务3.4.x稳定性经过验证,支持更久
大型企业合规要求3.3.x(尽快升级)保持低风险,但要注意支持到期
全 GraalVM 原生项目3.5.xAOT 编译改进明显
依赖 Spring Cloud 的微服务3.4.x + 2024.1.x版本对应矩阵最清晰

这里还要强调,Spring Cloud 和 Boot 的版本对应关系有时比 Boot 本身还重要。2024.0.x 支持 Boot 3.3,2024.1.x 支持 Boot 3.4,而 2025.0.x 才会对应 Boot 3.5。如果团队里 Spring Cloud 用的是旧版本,那强行升级 Boot 会造成无解的 jar 冲突。解决办法是先升级 Spring Cloud BOM,验证无误后再升级 Boot,这个顺序不要反。

4.3 长期演进的三条铁律

第一,不要只依赖自动迁移工具。spring-boot-properties-migrator能帮你找配置变更,但没法帮你测试@Transactional的失效问题、AOP 切面顺序改变、以及某些@Cacheable在代理模式下的行为差异。这些只有通过真实业务场景的回归测试才能发现。

第二,做好依赖锁定。把 Maven 的dependencyManagement或者 Gradle 的platform()作为唯一入口,禁止直接改子模块里的依赖版本。我在多个项目里见到过“因为某个模块需要不同 Jackson 版本,就直接在 pom 里全局声明”的做法,结果升级 Boot 时崩得一塌糊涂。锁定 BOM,才能让版本边界清晰。

第三,建立和升级配套的监控基线。每次升级后,至少在非生产环境跑一周的真实流量影子模式,观察这几个指标:接口 P99 延迟、GC 频率、线程池活跃度、连接池水位、Actuator 里的health状态变化。尤其是从 3.3 升到 3.4 时,由于默认线程池配置微调,并发高的系统很可能出现 Tomcat 最大线程数从原来的 200 变成 400 的隐性变化,如果没监控,线上就容易因为线程数暴增导致 CPU 峰值。

4.4 这个主题还能怎么延伸:从 Boot 到云原生生态

写了这么多版本对比,最后我想说一个观察:Spring Boot 的版本演进,本质上是在为云原生时代铺路。3.5 后你会发现官方把越来越多的重点放在模块化镜像、配置加密、链路追踪和观察能力上,这些正是 Kubernetes 环境下应用分散、生命周期短、自动扩缩容频繁所必须的底板能力。

如果你在规划未来三到五年的技术目标,我不建议只盯着版本号,而要关注几个方向:一是 GraalVM native 与容器的更好融合,二是虚拟线程运行时下的数据访问层优化,三是适配 JDK 长期支持版本(LTS)带来的内存和线程模型变化。把这些方向和版本升级绑在一起,你的架构才不会被某个具体版本锁死。

我个人在实际项目中的做法是,把版本升级当成一次小型重构来做,专门建立一个分支,把 Boot 和 Framework 的版本变更单独列成一个 checklist,每次升级至少预留两天的回归测试时间,绝不压缩这个步骤。等 3.5.x 正式版发布并迭代了两个补丁后,我会先把两个新项目建成 3.5,老核心系统继续停留在 3.4,慢慢用六到八周的时间窗口平滑迁移。这样既享受新特性,也不会被激进的升级带进沟里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询