前言
最近在做Spring Boot 4升级的时候,发现一个非常普遍的现象------大部分人被Jackson 3这个"隐形炸弹"炸得措手不及。
项目启动后,控制台报了一堆莫名其妙的错。
翻来覆去一看,原来不是业务代码的问题,是Jackson升级带来的破坏性变更。
"Jackson不就是个JSON序列化工具吗?升级一下能有什么大不了的?"
如果你这么想,那说明你还没真正理解Jackson在Java生态中的位置。
Jackson不仅仅是"一个JSON库",它是Java生态中事实上的JSON标准实现。
Spring Boot、微服务通信、Redis序列化、日志解析------几乎所有涉及到JSON的地方,底层都在用Jackson。
从Jackson 2.x升级到3.x,不是一次"无感升级"。
它是Jackson时隔8年的第一次大版本号变更。
从2017年底开始酝酿,到2025年10月3日正式发布GA版本,前后经历了10个RC版本。
今天这篇文章,我就把Jackson 3的新功能和迁移要点从头到尾给你拆解一遍。
希望对你会有所帮助。
更多项目实战在我的技术网站:susan.net.cn/project
一、Jackson 3到底"新"在哪?
在聊具体功能之前,我们先建立一个整体认知。
Jackson 3是一次价值取向非常明确的重构型演进。
如果说Jackson 2更偏向"兼容一切历史包袱",那么Jackson 3的目标非常清晰------安全、类型清晰、面向现代Java。

这张图基本概括了Jackson 3的核心变化。
下面我逐一给你拆解。
二、JDK基线升级到Java 17
这是Jackson 3最根本的变化。Jackson 3.x要求JDK 17+。
Jackson 2.x最低支持JDK 8。
从JDK 8到JDK 17,中间隔了9年、4个LTS版本。
这个升级意味着Jackson 3可以充分利用Java 17的新特性------Records、Sealed Classes、Pattern Matching等。
对你的影响 :如果你的项目还在JDK 8或11上,想用Jackson 3,必须先升级JDK。好消息是,Spring Boot 3.x已经要求JDK 17了,所以大部分新项目其实已经满足了这个条件。
三、包名和groupId全面变更
这是最"显眼" 的变更,也是你升级时第一个会碰到的编译错误。
| 对比项 | Jackson 2.x | Jackson 3.x |
|---|---|---|
| Maven groupId | com.fasterxml.jackson |
tools.jackson |
| Java包名 | com.fasterxml.jackson.xxx |
tools.jackson.xxx |
| 注解包名 | com.fasterxml.jackson.annotation |
保持不变 |
java
// Jackson 2.x 的写法
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonParser;
// Jackson 3.x 的写法
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.core.JsonParser;
为什么注解包名不变?
这是一个非常巧妙的设计。Jackson团队把注解库 (jackson-annotations)留在了原来的groupId和包名下,只把核心处理逻辑移到了tools.jackson。Jackson 3.0使用的是jackson-annotations 2.20版本。
这意味着:Jackson 2和Jackson 3可以在同一个项目中共存。你的核心应用可以用Jackson 3,老旧第三方依赖库依然可以用Jackson 2,互不干扰。
但要注意一个例外:jackson-databind内部的注解 (如@JsonSerialize、@JsonDeserialize)会移动到新包tools.jackson.databind.annotation。
四、ObjectMapper变成不可变的JsonMapper
这是Jackson 3影响面最大的变更。
在Jackson 2.x中,ObjectMapper是可变的。你可以这样写:
java
// Jackson 2.x - 可变配置
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 随时可以改
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 状态可以在任何时候被修改
这种设计在多线程环境下会引发问题------一个线程修改了配置,另一个线程正在使用,就会出现不可预知的行为。
Jackson 3.x强制使用Builder模式 ,构建完成后配置就被锁定了:
java
// Jackson 3.x - 不可变Builder模式
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build(); // 配置一旦build()就被锁定

必须使用格式对齐的子类 :在Jackson 3中,必须使用与数据格式匹配的ObjectMapper子类:
java
// Jackson 2.x - 可以混用
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
// Jackson 3.x - 必须使用对应的Mapper
JsonMapper jsonMapper = JsonMapper.builder().build(); // JSON
YAMLMapper yamlMapper = YAMLMapper.builder().build(); // YAML
XmlMapper xmlMapper = XmlMapper.builder().build(); // XML
禁止 new ObjectMapper(new YAMLFactory())这种写法。
五、异常体系重构
Jackson 2.x使用的是检查型异常(Checked Exception) ------JsonProcessingException是Exception的子类,你必须显式try-catch或throws。
Jackson 3.x改为非检查型异常(Unchecked Exception) :
| Jackson 2.x | Jackson 3.x |
|---|---|
JsonProcessingException |
JacksonException(基类) |
JsonParseException |
StreamReadException |
JsonEOFException |
UnexpectedEndOfInputException |
java
// Jackson 2.x - 必须处理检查型异常
try {
User user = mapper.readValue(json, User.class);
} catch (JsonProcessingException e) {
// 必须处理
}
// Jackson 3.x - 非检查型异常,可以选择不捕获
User user = mapper.readValue(json, User.class);
// 如果出错,抛出JacksonException(运行时异常)
这个改动让Jackson的API更符合现代Java编程习惯,和Spring、Lombok等主流框架保持一致。
六、默认配置全面调整
Jackson 3改了大量默认配置。
我挑几个最可能"炸到你"的来说。
6.1 日期序列化:从时间戳变成ISO-8601字符串
这是最容易被忽略、影响面最大的变更之一。
java
// Jackson 2.x 默认:2026-07-20 12:00:00 → 1721462400000(时间戳)
// Jackson 3.x 默认:2026-07-20 12:00:00 → "2026-07-20T12:00:00Z"(ISO-8601字符串)
如果你的前端代码依赖时间戳格式,升级后接口返回的数据格式会变。好消息是,可以显式配置改回来:
java
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // 改回时间戳
.build();
6.2 原始类型空值:从宽松变成严格
Jackson 3.x将FAIL_ON_NULL_FOR_PRIMITIVES的默认值从false改为了true。
java
// 假设你有这样的DTO
public class User {
private int age; // 原始类型int
}
// Jackson 2.x:JSON中age为null时,默认赋值为0
// Jackson 3.x:JSON中age为null时,抛出JsonMappingException
这个变更会让很多"以前能跑"的代码突然报错。解决方案:要么把int改成Integer,要么显式禁用这个特性。
6.3 月份计数:从0基变成1基
Java的java.util.Date中,月份是从0开始的(0=一月)。Jackson 3.x将DateTimeFeature.ONE_BASED_MONTHS的默认值从false改为了true。
这意味着序列化/反序列化时,月份的处理方式发生了变化。如果你的代码依赖"0=一月"的逻辑,升级后需要特别注意。
七、性能与内存优化
Jackson 3不只是"改API",在性能和内存方面也做了大量优化。
7.1 BeanDescription懒加载优化
Jackson 3.0对BeanDescription的实现引入了Supplier模式实现延迟加载 。对于简单类型或不需要完整BeanDescription的场景,避免了不必要的解析开销。这减少了临时对象的创建和GC压力。
7.2 RecyclerPool延迟初始化
Jackson Core 3.0优化了RecyclerPool的初始化策略。在高频使用Jackson的应用中,能显著提升内存使用效率。
7.3 字段名去重禁用
Jackson 3.0将TokenStreamFactory.Feature.INTERN_FIELD_NAMES的默认值改为false。这意味着JSON字段名不再被自动缓存到字符串常量池中,减少了内存占用。
八、模块整合:少了三个依赖
在Jackson 2.x中,为了支持Java 8的新特性,你需要额外引入三个模块:
xml
<!-- Jackson 2.x 需要额外引入 -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Jackson 3.x中,这三个模块已经整合到jackson-databind中。不需要单独引入了。
##九、破坏性变更速查表
我把Jackson 3中最可能让你的代码报错的变更整理成了一张速查表:
| 变更类型 | Jackson 2.x | Jackson 3.x | 影响 |
|---|---|---|---|
| 基类异常 | JsonProcessingException(检查型) |
JacksonException(非检查型) |
try-catch需要改 |
| ObjectMapper | 可变,new即可 | 不可变,必须Builder | 所有创建代码要改 |
| 包名 | com.fasterxml.jackson |
tools.jackson |
所有import要改 |
| groupId | com.fasterxml.jackson.core |
tools.jackson.core |
pom.xml要改 |
| 日期默认 | 时间戳 | ISO-8601字符串 | 接口返回格式变 |
| 原始类型null | 允许(赋默认值) | 抛异常 | 可能新增报错 |
ObjectCodec |
存在 | 已移除 | 强转代码报错 |
JsonGenerator.writeObject() |
存在 | 改为writePOJO() |
方法名要改 |
JsonParser.getCurrentLocation() |
存在 | 改为currentLocation() |
方法名要改 |
十、优缺点与适用场景
Jackson 3的优点
1. 线程安全有保障 JsonMapper不可变,构建完成后配置锁定,可以在多线程环境下安全共享。
2. 面向现代Java JDK 17+基线,可以充分利用Records、Sealed Classes等新特性。
3. 默认配置更安全 日期输出ISO-8601更符合国际标准,原始类型空值检查更严格,多态类型验证更严格。
4. 性能优化 BeanDescription懒加载、RecyclerPool延迟初始化等多项优化。
5. 依赖精简 jackson-module-parameter-names、jackson-datatype-jdk8、jackson-datatype-jsr310已整合到databind。
6. 异常处理更简洁 非检查型异常,不需要到处try-catch。
7. 生态正在快速跟进 Spring Boot 4已默认使用Jackson 3,Netflix DGS等主流框架也已支持。
Jackson 3的注意事项
1. 不是LTS版本 Jackson 3.0是过渡版本,3.1才是第一个LTS版本。生产环境建议直接升级到3.1.x。
2. 破坏性变更极多 包名、类名、方法名、异常体系、默认配置全面变化,升级工作量不小。
3. 生态依赖拖后腿 很多第三方库还在用Jackson 2,比如Swagger。好消息是Spring Boot 4同时管理Jackson 2和3的依赖,可以共存。
4. 迁移需要系统规划 建议使用OpenRewrite自动化迁移工具 ,或者配合Spring的spring.jackson.use-jackson2-defaults配置逐步过渡。
适用场景
| 场景 | 推荐程度 | 理由 |
|---|---|---|
| 新项目用Spring Boot 4 | ✅✅✅ 强烈推荐 | Spring Boot 4默认就是Jackson 3 |
| 追求线程安全的高并发系统 | ✅✅✅ 强烈推荐 | JsonMapper不可变,线程安全 |
| 需要现代Java特性 | ✅✅✅ 强烈推荐 | Records、Sealed Classes等 |
| 老项目升级Spring Boot 4 | ⚠️ 需谨慎规划 | 破坏性变更多,建议用OpenRewrite辅助迁移 |
| 依赖大量Jackson 2生态库 | ⚠️ 需评估 | 虽然可以共存,但需要额外配置 |
十一、迁移建议
如果你准备升级到Jackson 3,我建议按这个顺序来:
第一步:升级JDK到17+
这是硬性要求。如果还在JDK 8/11,先升级JDK。
第二步:升级Spring Boot到4.x
Spring Boot 4已经全面拥抱Jackson 3。Spring Boot 4同时管理Jackson 2和3的依赖,可以平滑过渡。
第三步:用OpenRewrite自动化迁移
bash
# OpenRewrite提供了Jackson 2→3的自动化迁移配方
mvn rewrite:run -DactiveRecipes=org.openrewrite.java.jackson.UpgradeJackson_2_3
第四步:利用过渡配置
Spring Boot 4提供了spring.jackson.use-jackson2-defaults配置,可以暂时保持Jackson 2的默认行为,作为过渡期的"安全网"。
第五步:逐步替换代码
- 改
import:com.fasterxml.jackson→tools.jackson(注解除外) - 改创建方式:
new ObjectMapper()→JsonMapper.builder().build() - 改异常处理:
JsonProcessingException→JacksonException - 检查日期序列化:确认前端兼容ISO-8601格式
更多项目实战在我的技术网站:susan.net.cn/project
十二、写在最后
回到最初的问题:Jackson 3来了,有哪些新功能?
它不是加了几个新注解、修了几个Bug那么简单。
Jackson 3是一次彻底的架构升级------从JDK基线到包名、从API设计到异常体系、从默认配置到性能优化,全部重新梳理了一遍。
Jackson 3.0于2025年10月3日正式发布GA版本,从2017年底开始酝酿,到正式发布经历了近8年的打磨。
而Spring Boot 4已经将其作为默认JSON库,整个Java生态正在加速向Jackson 3迁移。
但升级前要做好心理准备------Jackson 3的破坏性变更非常多。包名变了、类名变了、方法名变了、异常类型变了、默认行为也变了。这不是一次"无感升级"。
好消息是,Spring Boot 4同时管理Jackson 2和3的依赖,可以让你逐步迁移。Jackson 2.21依然是LTS版本,短期内不会消失。
我的建议是 :新项目直接上Jackson 3;老项目跟随Spring Boot 4升级,利用spring.jackson.use-jackson2-defaults过渡,逐步完成迁移。如果依赖库太多、短期内无法全部升级,Jackson 2和3共存也是可行的方案。