# Spring Boot 3.2.5 → 4.1.1 升级说明 升级日期:2026-10-19 分支:`feature/spring-boot-4`(基于 `develop` 的 `147bbe6`,即已升到 3.5.16 的提交) ## 一、版本变化 | 组件 | 升级前 | 升级后 | |---|---|---| | Spring Boot | 3.2.5 | **4.1.1** | | Spring Framework | 6.1.x | 7.0.9(Boot 传递) | | Spring Security (crypto) | 6.2.x | 7.0.x | | Jakarta EE 基线 | 10(Servlet 6.0 / Tomcat 10.1) | **11(Servlet 6.1 / Tomcat 11.0.24)** | | Jackson | 2.15 | **3.1.5**(`tools.jackson.*`) | | MyBatis-Plus | 3.5.5 / spring-boot3-starter | **3.5.17 / spring-boot4-starter** | | JDK | 21 | 21(不变,Boot 4 基线为 17) | 升级前先单独落了一版 3.5.16(提交 `147bbe6`),确认零改动即可通过全部回归,再进 4.x,避免跨两个大版本混在一起排错。 ## 二、代码改动(58 文件) ### 1. Jackson 包名迁移(54 文件,纯 import) `com.fasterxml.jackson.databind|core` → `tools.jackson.databind|core`。 **注解不动**:`@JsonProperty` / `@JsonIgnore` / `@JsonInclude` / `@JsonView` 仍在 `com.fasterxml.jackson.annotation`(Jackson 3 为向后兼容刻意保留),涉及 63 个文件一行未改。 批量脚本:`tools/migrate_jackson3.py`(可重复执行,`--dry` 只统计不写)。 ### 2. 两个自定义序列化器改写 Jackson 3 把 `JsonSerializer` 更名 `ValueSerializer`,第三个参数换成 `SerializationContext`, 异常由受检的 `IOException` 改为非受检的 `JacksonException`: ```java // 旧 public class PhpFloatSerializer extends JsonSerializer { public void serialize(Double v, JsonGenerator gen, SerializerProvider p) throws IOException // 新 public class PhpFloatSerializer extends ValueSerializer { public void serialize(Double v, JsonGenerator gen, SerializationContext ctxt) throws JacksonException ``` - `common/util/PhpFloatSerializer.java` - `common/util/UserLiteSerializer.java`(另:`new ObjectMapper().setSerializationInclusion(..)` 不可用,改为 `JsonMapper.builder().build()`;默认包含策略本就是 ALWAYS) 这两个类被 8 处 `@JsonSerialize(using = ...)` 引用,改完实体侧无需改动。 ### 3. Redis 序列化器重写 `config/RedisConfig.java`: - `new ObjectMapper()` + `setVisibility(..)` + `enableDefaultTyping(NON_FINAL)` 全部不可用 (Jackson 3 的 mapper 不可变,这些方法已移除)。 - 改用 Spring Data Redis 4 新增的 `GenericJacksonJsonRedisSerializer`: ```java GenericJacksonJsonRedisSerializer.builder().enableUnsafeDefaultTyping().build() ``` 语义等价于原 `GenericJackson2JsonRedisSerializer` / default typing(写 `@class` 类型信息), `RedisTemplate` 与 `RedisCacheManager` 共用同一个工厂方法。 - `config/RedisConfigCacheManager.java` 的 `DEFAULT_PAIR` 同步换成 `RedisConfig.jsonSerializer()`。 > 注:项目内 `@Cacheable` 用量为 0,缓存管理器实际未被业务使用,因此即便有旧缓存格式差异也不影响业务。 ### 4. MyBatis-Plus 两处适配 1. **分页插件拆包**:MP 3.5.9 起 `PaginationInnerInterceptor` 移到独立模块,需显式引入 `com.baomidou:mybatis-plus-jsqlparser`(版本随 `mybatis-plus.version`)。 不加会报 `cannot find symbol: class PaginationInnerInterceptor`。 2. **JSON TypeHandler 换 Jackson 3 版**:`Goods.deliveryType` 原来用 `JacksonTypeHandler`(Jackson 2 实现), Boot 4 不再带 Jackson 2,启动时会 `NoClassDefFoundError: com/fasterxml/jackson/core/JacksonException`。 改为 MP 3.5.17 提供的 `Jackson3TypeHandler`。 ### 5. Lombok 注解处理器需显式声明 Boot 4 的 `maven-compiler-plugin` 3.15.0 不再从 classpath 自动发现注解处理器, 只配 `true` 时 `@Data` 完全不生成方法(一开始报了 200 个 "cannot find symbol: getXxx")。pom 里补: ```xml org.apache.maven.plugins maven-compiler-plugin org.projectlombok lombok ${lombok.version} ``` ### 6. Web starter 更名 Boot 4 把 `spring-boot-starter-web` 标为废弃,pom 描述原文: > Starter for building web, including RESTful, applications using Spring MVC. > Uses Tomcat as the default embedded container (**deprecated in favor of spring-boot-starter-webmvc**) 所以 pom 换成 `spring-boot-starter-webmvc`。两者在 4.1.1 中解析出的依赖集合**完全等价**: | 项 | starter-web | starter-webmvc | |---|---|---| | starter-jackson | ✓ | ✓ | | starter-tomcat | ✓ | ✓ | | spring-boot-http-converter | ✓ | ✓ | | spring-boot-webmvc | ✓ | ✓ | | spring-boot-starter | ✗(靠 starter-validation 传递) | ✓(直接声明) | 实测 `mvn dependency:tree` 前后各 100 个构件,唯一差异就是 starter 自身坐标; 换名后回归 390 项仍全过。早先版本(3.x)没有 `starter-webmvc` 这个坐标, 只有在 Boot 4 上才能换。 ## 三、不受影响的部分(已核实) - **时间格式**:实体时间字段是 `Integer` 时间戳 + `getCreateTimeText()` 手工 `TimeUtil.format`, 不走 Jackson 日期序列化。Jackson 3 把 `WRITE_DATES_AS_TIMESTAMPS` 默认改为 ISO-8601 的变化 对本项目出参**零影响**,`spring.jackson.date-format` 保留即可。 - **`@JsonView`**:`spring.jackson.mapper.default-view-inclusion: true` 在 Boot 4 仍有效 (已实测 `goods/list` 不含 `content`、`goods/detail` 含 `content`)。 - **`spring.jackson.date-format` / `time-zone` / `mapper.*`**:配置项在 4.1.1 中均仍存在。 - **`ObjectNode.remove(Collection)` / `createObjectNode()` / `valueToTree()`**:签名未变,`ApiView` 只改了 import。 - **多模块拦截器**:`WebMvcConfig` / `StoreAuthInterceptor` / `AdminAuthInterceptor` / `ApiAuthInterceptor` 未改。 - **PHP `$2y$` 口令**:Spring Security 7 的 `BCryptPasswordEncoder` 仍能校验 (用 PHP 8.2 `password_hash` 生成 `$2y$` 哈希实测登录成功)。 ## 四、唯一的行为变化(可接受) Jackson 3 默认开启 `SORT_PROPERTIES_ALPHABETICALLY`,实体经 `valueToTree` 后**字段顺序变为字母序** (如 `goodsId` 之前排在 `goodsName` 前,现在 `aloneGradeEquity` 开头)。字段名与值不变, 前端按名取值不受影响;手工 `put` 的 `ObjectNode` 仍保持插入顺序。 旧的 `compare_*.py` 若按字节比 JSON 会误报,这些脚本已随驼峰改造废弃。 ## 五、回归结果(全部在 Boot 4.1.1 上跑) | 脚本 | 内容 | 结果 | |---|---|---| | `test_api_batch1.py` | api 只读接口 | 52 / 0 | | `test_api_batch2.py` | 购物车/结算/订单/地址 | 97 / 0 | | `test_api_batch3.py` | 用户中心/售后/充值/上传 | 99 / 0 | | `test_api_batch4.py` | 登录/验证码/收银台/支付回调 | 77 / 0 | | `test_admin_api.py` | 平台后台 | 45 / 0 | | `test_token_user.py` | store 登录态(Redis) | 20 / 0 | | `smoke_camel.py` | 驼峰冒烟 | 全部通过 | 合计 **390 项断言,0 失败**。 ## 六、回滚方式 ```bash git checkout develop # Boot 3.5.16,随时可发布 # 或 git checkout feature/spring-boot-4 ``` ## 七、后续可选 - Boot 4 仍保留 `spring-boot-jackson2` 模块(`spring.jackson2.*` 属性), 万一某个第三方库只支持 Jackson 2,可用它过渡,但属废弃路径,不建议长期依赖。 - 若将来引入 `@Cacheable`,注意 Boot 4 + Jackson 3 的缓存值格式与 Jackson 2 的 `GenericJackson2JsonRedisSerializer` 不完全一致(`@class` vs 数组式 typing),切流时先清缓存。