对于资深 Spring Boot 开发者来说,Jackson 无疑是处理 Java 对象与 JSON 转换的事实标准。在 Spring Boot 4 中,一个显著的变化是默认的 JSON 库从 Jackson 2 切换到了 Jackson 3。
这并非一次简单的版本号升级,其依赖结构也发生了有趣的变化:
spring-boot-starter-jackson (4.x)
├── tools.jackson.core:jackson-core:3.x ← Jackson 核心库
└── com.fasterxml.jackson.annotation:jackson-annotations:2.x ← Jackson 注解
很多开发者在第一次查看项目依赖树时都会感到困惑:为什么项目里同时存在 Jackson 2 和 Jackson 3?
这其实是 Jackson 团队为了解决一个非常现实的工程问题而采取的策略:整个 Java 生态不可能一夜之间全部迁移到新版本。他们的解决方案非常巧妙:
这意味着你熟悉的 @JsonView、@JsonFormat、@JsonIgnore 等注解完全不需要修改,这种设计允许项目在 Jackson 2 和 3 之间进行渐进式、无痛迁移。
Spring Boot 4 默认升级到 Jackson 3 后,下面这 4 个核心变化,每一个都可能成为你升级路上的“坑点”,需要特别注意。
这是最直观、也最容易导致编译错误的变化。
// Jackson 2
import com.fasterxml.jackson.databind.ObjectMapper;
// Jackson 3
import tools.jackson.databind.ObjectMapper;
重要提示:只有核心 API 的包名发生了变化,所有注解(如 @JsonProperty)依然保留在 com.fasterxml.jackson.annotation 包下。
2. ObjectMapper 隐退,JsonMapper 成为主角
在 Jackson 2 中,ObjectMapper 是可变的(Mutable),这在多线程环境下存在安全隐患:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 修改了实例状态
Jackson 3 则强制推行 Builder + Immutable(不可变) 模式,这本身就是一种优秀的 System Design 实践,能提升应用的健壮性:
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build(); // 构建后实例不可变
一旦调用 build() 方法,配置便被锁定,生成的 JsonMapper 实例可以安全地在多线程间共享,天然具备线程安全性。这是 Jackson 3 在 API 设计上一次重要的升级。
在 Jackson 3 中,直接 new ObjectMapper() 得到的将是仅包含默认配置的实例。如需自定义配置,必须使用 JsonMapper.builder() 来创建不可变的、线程安全的实例。
3. 日期序列化默认格式变化(极易踩坑)
这是向后兼容性方面最容易出问题的一点。Jackson 3 更改了日期/时间类型的默认序列化格式。
| 版本 |
默认序列化格式 |
| Jackson 2 |
时间戳(如 1767588151648) |
| Jackson 3 |
ISO-8601 字符串(如 "2026-01-05T02:02:31.648Z") |
// Jackson 2 默认输出
{"nowDate": 1767588151648}
// Jackson 3 默认输出
{"nowDate": "2026-01-05T02:02:31.648Z"}
对前端而言,ISO-8601 格式的字符串更友好、更易读。但这会导致两个常见问题:
- 依赖精确时间戳进行断言的旧测试用例会失败。
- 与特定时间格式强绑定的接口契约或 Mock 数据需要同步调整。
临时兼容方案:在迁移初期,可以通过配置让 Jackson 3 暂时保持 Jackson 2 的默认行为。
spring:
jackson:
use-jackson2-defaults: true
4. 告别 Checked Exception
这个改动让许多 Java 开发者拍手称快。Jackson 2 中,许多方法会抛出 IOException 这类受检异常(Checked Exception),代码不得不包裹在 try-catch 块中:
// Jackson 2
try {
objectMapper.readValue(json, MyClass.class);
} catch (IOException e) {
// 异常处理
}
而在 Jackson 3 中,所有异常都继承自 JacksonException,而它是一个运行时异常(RuntimeException):
// Jackson 3
jsonMapper.readValue(json, MyClass.class); // 无需捕获受检异常
最直接的好处体现在现代 Java 的流式编程中,代码变得更加简洁:
// Jackson 2:在lambda中直接抛出受检异常会导致编译错误
list.stream()
.map(o -> objectMapper.writeValueAsString(o)) // 编译报错!
.toList();
// Jackson 3:完全没问题,代码更优雅
list.stream()
.map(o -> jsonMapper.writeValueAsString(o))
.toList();
这一设计充分考虑了现代 Java 的编码习惯,让 API 用起来更加顺手。
总结
Spring Boot 4 拥抱 Jackson 3 是一次重要的生态升级,带来了更安全的 API 设计、更现代的异常处理机制。升级时,请重点关注包名变更、不可变的 JsonMapper、日期格式默认值以及异常类型这四个核心变化点。合理利用 use-jackson2-defaults 配置可以平滑过渡。理解这些变化,能帮助你在技术升级浪潮中更加从容。如果你想深入探讨这些技术细节或获取更多实战项目经验,欢迎到 云栈社区 与更多开发者交流。