文章目录
SmartOA 后端升级到 Spring Boot 4 后,Jackson 也迎来了大版本跃迁:Jackson 2.x → 3.x(tools.jackson)。包名从 com.fasterxml.jackson 换成 tools.jackson,序列化器/反序列化器/ObjectMapper 全部要改。后端 1797 个 Java 文件里,受影响的就有 405 个。这是一次典型的「升级连带改造」,记录完整的手术过程和依赖升级纪律。
一、为什么动 Jackson
Spring Boot 4 的默认 JSON 序列化已经切换到 Jackson 3(tools.jackson),旧包 com.fasterxml.jackson.databind 在新框架下成了遗留依赖。如果继续用 Jackson 2,会出现两个 ObjectMapper 并存、注解不生效等诡异问题。既然要上 Spring Boot 4,Jackson 3 就必须同步迁。
二、迁移前:盘点影响面
先做全量扫描,摸清两类改动点:
# 1. 包名引用:com.fasterxml.jackson → tools.jackson
grep -rl "com.fasterxml.jackson" backend/ | wc -l
# 输出:405 个文件
# 2. 自定义序列化器/反序列化器(逻辑可能不兼容,需逐个看)
grep -rln "extends JsonSerializer\|extends JsonDeserializer" backend/
405 个文件里,绝大多数是机械的 import 替换;真正需要人工判断的是自定义序列化器/反序列化器和 ApiEncrypt 加解密链路。
三、批量替换:405 个文件的 import
包名替换用 IDE 全局替换 + 正则收尾,替换规则:
| 旧包 | 新包 |
|---|---|
| com.fasterxml.jackson.databind | tools.jackson.databind |
| com.fasterxml.jackson.core | tools.jackson.core |
| com.fasterxml.jackson.annotation | tools.jackson.annotation |
替换后立即 mvn compile,编译器会帮你找出漏网之鱼。注意:不是所有 com.fasterxml 都无脑替换——项目里如果还有第三方库内部依赖 Jackson 2,它们的类路径不能动,只改自己代码里的 import。
四、真正的坑:自定义序列化器与 ApiEncrypt
机械替换之后,编译过了,但测试开始报错。问题集中在三类:
坑 1:序列化器/反序列化器 API 变化
Jackson 3 里 JsonSerializer.serialize() 的签名有变化(Generator 参数类型调整),自定义序列化器需要适配:
// Jackson 2 时代
public class XxxSerializer extends JsonSerializer<Xxx> {
@Override
public void serialize(Xxx value, JsonGenerator gen, SerializerProvider p) {
gen.writeString(value.getCode());
}
}
// Jackson 3(tools.jackson):Generator 类型与异常声明调整
public class XxxSerializer extends JsonSerializer<Xxx> {
@Override
public void serialize(Xxx value, JsonGenerator gen, SerializerProvider p)
throws IOException {
gen.writeString(value.getCode());
}
}
坑 2:ObjectMapper 注入方式
项目里多处 @Autowired ObjectMapper 或自己 new 的 ObjectMapper,Jackson 3 的 ObjectMapper 构建方式变了(builder 模式更彻底):
// ❌ 旧习惯:new ObjectMapper() + 手动注册模块
var mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// ✅ Jackson 3:JsonMapper.builder() 链式构建
var mapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();
坑 3:ApiEncrypt 加解密链路
接口加解密依赖自定义序列化器在出参时加密、入参时解密。Jackson 3 迁移后,反序列化器的读取逻辑和异常类型都变了,解密失败从静默返回空串改为抛 RuntimeException(M3 修复)——这反而是个改进:以前解密失败静默返回空串,等于把「解密失败」伪装成「数据为空」,绕过密码校验;现在直接抛异常,问题立刻暴露。
五、顺带清理:405 个文件的重复 import
迁移过程中发现大量文件存在重复 import(历史遗留),mvn compile 不报但代码很脏。这次一并清理:405 个 Java 文件去重。用 Spotless 统一格式化兜底,保证后续 diff 干净。
六、连带修复:Druid 双重初始化 + BouncyCastle 重复注册
升级期间顺手修了两个严重 bug 风险:
- Druid 双重初始化:数据源初始化逻辑被触发两次,连接池出现重复创建
- BouncyCastle 重复注册:加密 Provider 重复 addProvider,可能导致 JCE 冲突
这两个都属于「平时不炸、升级才炸」的定时炸弹,大版本升级是最好的排查窗口。
七、迁移清单(可直接复用)
- 先盘点再动手:grep 统计影响文件数(405),区分「机械替换」和「人工适配」
- 包名替换走 IDE:全局替换 + 编译器兜底,别手改
- 自定义序列化器逐个看:签名变化 + 异常声明,不能只看编译过没过
- ObjectMapper 构建方式:builder 模式,模块注册方式同步改
- 安全链路优先测:加解密、脱敏这类序列化相关功能写回归测试,升级后先跑
- 顺手清理:重复 import、旧 API 调用,升级窗口期一并处理
评 论