做 SpringBoot 开发,几乎每天都在和 JSON 序列化打交道:
@RestController 返回对象自动转 JSON、@RequestBody 自动反序列化、配置文件里调日期格式……
但你有没有想过一个问题:
Java 生态里 JSON 库这么多——Gson、Fastjson、Fastjson2、org.json……为什么 SpringBoot 偏偏选了 Jackson 当默认?而且一用就是十几年,从来没换过?
有人说 Fastjson 性能比 Jackson 快,有人说 Gson API 比 Jackson 简洁,还有人说 Jackson 又重又难用。
但 SpringBoot 从 1.x 到现在的 3.x/4.x,默认序列化库始终是 Jackson,哪怕 Fastjson2 性能再强、Gson 再简洁,Spring 官方也从来没动过替换的念头。
这背后到底是什么原因?是技术选型的惯性,还是 Jackson 真的有不可替代的优势?
一、SpringBoot 里的 Jackson 是怎么存在的
很多人用了很久 SpringBoot,都没意识到自己一直在用 Jackson。
1.1 默认引入,零配置生效
只要你引入了 spring-boot-starter-web,Jackson 就被自动带进来了:
spring-boot-starter-web
└── spring-boot-starter-json
├── jackson-databind ← 核心数据绑定
├── jackson-core ← 流式解析/生成
├── jackson-annotations ← 注解支持
└── jackson-datatype-jsr310 ← Java 8 时间类型支持
SpringBoot 的 JacksonAutoConfiguration 自动配置类会自动创建 ObjectMapper Bean,HttpMessageConverters 自动注册 MappingJackson2HttpMessageConverter。
你什么都不用配,@RestController 就能直接返回 JSON。
1.2 想换掉它?可以,但很麻烦
SpringBoot 也支持切换到 Gson 或 JSON-B,但需要:
-
1. 排除 Jackson 依赖; -
2. 引入 Gson 依赖; -
3. 配置 spring.mvc.converters.preferred-json-mapper=gson; -
4. 处理各种 Jackson 注解( @JsonProperty、@JsonIgnore等)不兼容的问题。
实际项目中,99% 的团队都不会去换,因为替换成本远大于收益。
二、Jackson 凭什么成为默认?
维度1:生态成熟度
Jackson 是 Java 生态中最成熟、应用最广的 JSON 库,没有之一。
几乎所有主流框架都深度集成 Jackson
-
• Spring Framework / Spring Boot:默认 JSON 库; -
• Hibernate / Spring Data JPA:延迟加载、实体序列化用 Jackson; -
• Spring Security:OAuth2、JWT 的 JSON 处理用 Jackson; -
• Spring Cloud:服务间调用、配置序列化用 Jackson; -
• Elasticsearch Java Client:新版本默认用 Jackson; -
• Kafka / RabbitMQ:JSON 消息序列化默认用 Jackson; -
• 大量第三方 SDK:OpenAI、Stripe、AWS 等官方 SDK 都用 Jackson。
这意味着什么?
你的项目里可能有十几个框架都在共用同一个 Jackson 的 ObjectMapper。
如果换掉 Jackson,这些框架的序列化行为都可能出问题,牵一发而动全身。
模块生态极其丰富
Jackson 有一个庞大的 Module 生态,几乎支持所有常见数据类型:
-
• jackson-datatype-jsr310:Java 8 时间类型(LocalDateTime 等); -
• jackson-datatype-jdk8:Optional、Stream 等; -
• jackson-datatype-guava:Guava 集合类型; -
• jackson-datatype-hibernate:Hibernate 懒加载代理; -
• jackson-datatype-joda:Joda-Time; -
• jackson-module-kotlin:Kotlin 数据类支持; -
• jackson-module-scala:Scala 支持; -
• jackson-dataformat-xml:XML 格式支持; -
• jackson-dataformat-yaml:YAML 格式支持; -
• jackson-dataformat-csv:CSV 格式支持。
这种生态广度,是 Gson、Fastjson 都比不了的。Gson 对 Java 8 时间类型的支持要自己写适配器,Fastjson 的模块生态更是远不如 Jackson。
维度2:架构设计
Jackson 的架构设计是它能长期屹立不倒的核心原因。它不是一个”大而全”的单体库,而是分层清晰、可插拔的架构。
三层架构
┌─────────────────────────────────────┐
│ jackson-databind(数据绑定层) │
│ ObjectMapper / 序列化器 / 反序列化器 │
├─────────────────────────────────────┤
│ jackson-core(流式核心层) │
│ JsonParser / JsonGenerator │
├─────────────────────────────────────┤
│ jackson-annotations(注解层) │
│ @JsonProperty / @JsonIgnore 等 │
└─────────────────────────────────────┘
-
• core 层:最底层的流式 API,只负责 JSON 的解析和生成,不关心数据绑定; -
• databind 层:基于 core 层实现对象和 JSON 的自动绑定,是最常用的 API; -
• annotations 层:注解定义,独立于 core 和 databind,可以单独引用。
这种分层带来的好处是:你可以只用 core 层做极致性能的流式处理,也可以用 databind 层做方便的对象映射,各取所需。
Module 机制:无限扩展
Jackson 的扩展能力来自于它的 Module 机制:
// 自定义一个 Module,注册自定义序列化器
SimpleModule module = new SimpleModule("MyModule");
module.addSerializer(MyType.class, new MyTypeSerializer());
module.addDeserializer(MyType.class, new MyTypeDeserializer());
objectMapper.registerModule(module);
任何第三方库都可以通过 Module 机制扩展 Jackson 的能力,而不需要修改 Jackson 源码。
SpringBoot 就是通过 Jackson2ObjectMapperBuilderCustomizer 来定制 ObjectMapper 的,本质上也是 Module 机制的应用。
可插拔的注解支持
Jackson 甚至支持使用其他库的注解:
-
• jackson-annotations:Jackson 自己的注解; -
• jackson-module-jaxb-annotations:支持 JAXB 注解(@XmlElement等); -
• jackson-module-jsonSchema:生成 JSON Schema。
这种高度可扩展的架构,让 Jackson 能适应各种复杂的业务场景,而不是只能做简单的对象映射。
维度3:性能
很多人拿 Fastjson 的性能 benchmark 说事,说 Jackson 性能不行。
但真实情况是:Jackson 的性能在绝大多数场景下完全够用,而且性能表现非常稳定。
性能对比(参考 JMH 基准测试,Java 17,常见业务对象)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Fastjson2 确实更快,但差距没有想象中那么大。
而且这个 benchmark 是纯内存的简单对象,真实业务场景中,JSON 序列化往往不是性能瓶颈——数据库查询、网络 IO、业务逻辑的耗时远大于序列化本身。
为了 20% 的序列化性能提升,去换掉整个生态默认库,引入兼容性风险,是典型的”过早优化”。
Jackson 的性能优化点
Jackson 也在持续优化性能:
-
• Afterburner/Blackbird模块:通过字节码生成替代反射,性能提升 20%~30%; -
• 流式 API( JsonParser/JsonGenerator):极致性能场景下可以直接用,比 databind 快很多; -
• 对象池、缓存机制:减少重复创建对象的开销。
SpringBoot 6.x 开始默认启用 Blackbird 模块(Java 17+ 支持),进一步提升了序列化性能。
维度4:安全性
JSON 库的安全漏洞是个大问题,尤其是反序列化漏洞,可能导致远程代码执行(RCE)。
Fastjson 的安全历史
Fastjson 历史上爆出过多次严重的反序列化漏洞(CVE-2017-18349、CVE-2020-8840 等),通过 @type 字段可以构造恶意 JSON 执行任意代码。
虽然 Fastjson2 做了安全加固,但历史包袱让很多企业对它心存顾虑。
Jackson 的安全设计
Jackson 在安全方面更加谨慎:
-
• 默认不启用多态类型处理( enableDefaultTyping),需要显式开启; -
• 即使开启多态,也有 PolymorphicTypeValidator做白名单校验; -
• 安全漏洞响应速度快,社区活跃,补丁发布及时; -
• Jackson 的反序列化漏洞相对较少,且大多需要特定配置才会触发。
对于 SpringBoot 这种被全球数百万企业使用的框架来说,安全性是比性能更重要的考量。
默认库的安全漏洞影响面太大,Spring 官方不可能不慎重。
维度5:Spring 深度集成
这是最关键的一点:Spring 不是简单地”选了 Jackson 当默认库”,而是整个 Spring 生态的很多设计都是围绕 Jackson 构建的。
Spring MVC 的消息转换器
MappingJackson2HttpMessageConverter 是 Spring MVC 最核心的 HTTP 消息转换器,它深度依赖 Jackson 的 API:
-
• 支持 @JsonView做视图过滤; -
• 支持 @RequestBody的ObjectMapper定制; -
• 支持 MediaType的精细控制; -
• 支持 Jsonp、JSON Patch等高级特性。
这些功能如果换成 Gson,很多都要重新实现,且行为不一致。
SpringBoot 的自动配置
JacksonAutoConfiguration 提供了丰富的配置项:
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
property-naming-strategy: SNAKE_CASE
serialization:
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
这些配置项都是基于 Jackson 的特性设计的,换成其他库根本不兼容。
Spring 的数据绑定体系
Spring 的 DataBinder、ConversionService、TypeConverter 等数据绑定组件,和 Jackson 的类型转换体系是深度协同的。
Jackson 自定义的 Serializer/Deserializer 可以和 Spring 的类型转换体系无缝配合。
一句话总结:
换掉 Jackson,不是换一个 JSON 库那么简单,而是要撼动整个 Spring 生态的根基。
这种迁移成本,没有任何一家框架维护者会愿意承担。
三、Jackson 你可能不知道的高级用法
很多人觉得 Jackson 难用,其实是只用了最基础的 objectMapper.writeValueAsString()。
Jackson 的能力远不止于此,下面是几个生产环境常用的高级技巧。
3.1 自定义序列化器:处理特殊类型
/**
* 自定义 LocalDateTime 序列化器,输出时间戳
*/
public class LocalDateTimeTimestampSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
gen.writeNumber(timestamp);
}
}
// 使用:在字段上标注
@JsonSerialize(using = LocalDateTimeTimestampSerializer.class)
private LocalDateTime createTime;
3.2 动态过滤字段:@JsonView
同一个对象,不同接口返回不同字段,不用建多个 VO:
public class Views {
public static class Simple {}
public static class Detailed extends Simple {}
}
@Data
public class UserVO {
@JsonView(Views.Simple.class)
private Long id;
@JsonView(Views.Simple.class)
private String username;
@JsonView(Views.Detailed.class)
private String phone;
@JsonView(Views.Detailed.class)
private String email;
}
// Controller 中指定视图
@GetMapping("/simple")
@JsonView(Views.Simple.class)
public UserVO getSimpleUser() { ... }
@GetMapping("/detailed")
@JsonView(Views.Detailed.class)
public UserVO getDetailedUser() { ... }
3.3 多态类型处理:父子类序列化
// 父类指定多态处理
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Circle.class, name = "circle"),
@JsonSubTypes.Type(value = Rectangle.class, name = "rectangle")
})
public abstract class Shape { }
public class Circle extends Shape {
private double radius;
}
public class Rectangle extends Shape {
private double width;
private double height;
}
序列化时会自动带上 type 字段,反序列化时根据 type 自动还原成对应的子类对象。
3.4 SpringBoot 中统一配置 ObjectMapper
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder
// 日期格式
.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
// 时区
.timeZone("GMT+8")
// 空值不序列化
.serializationInclusion(JsonInclude.Include.NON_NULL)
// 蛇形命名
.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
// 未知属性不报错
.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
// 日期不输出时间戳
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
// 注册 Java 8 时间模块
.modulesToInstall(new JavaTimeModule());
};
}
}
四、Jackson vs Gson vs Fastjson2到底怎么选?
虽然 SpringBoot 默认用 Jackson,但不代表你的项目里只能用 Jackson。
不同场景有不同的最优选择。
4.1 横向对比
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.2 选型建议
✅ 默认用 Jackson:
-
• SpringBoot 项目,没有特殊需求就用默认的,不要折腾; -
• 企业级项目,需要稳定、安全、生态丰富; -
• 和 Spring 生态深度绑定的项目。
✅ 用 Gson:
-
• Android 开发(Gson 体积小,适配性好); -
• 简单的 JSON 处理,不需要复杂特性; -
• 非 Spring 项目,追求 API 简洁。
✅ 用 Fastjson2:
-
• 对 JSON 序列化性能有极致要求的场景(比如网关、高性能中间件); -
• 国内项目,团队熟悉 Fastjson; -
• 注意安全配置,关闭 autoType,做好白名单。
重要提醒:
不要在一个项目里混用多个 JSON 库。
Jackson 转的对象用 Gson 反序列化,注解不兼容、行为不一致,排查问题会非常痛苦。
选定一个,统一用到底。
五、注意事项
1:LocalDateTime 序列化失败
现象:SpringBoot 2.x 升级到 3.x 后,LocalDateTime 序列化报错或格式不对。
根因:没有注册 JavaTimeModule,或者日期格式配置不对。
✅ 解决:确保引入 jackson-datatype-jsr310(SpringBoot 默认已引入),配置 spring.jackson.serialization.write-dates-as-timestamps=false。
2:循环引用导致栈溢出
现象:双向关联的实体(比如订单和订单项)序列化时 StackOverflowError。
根因:A 引用 B,B 又引用 A,序列化时无限递归。
✅ 解决:用 @JsonManagedReference / @JsonBackReference 标注双向关系,或用 @JsonIgnore 忽略一方。
3:字段名和 JSON key 不匹配
现象:Java 字段是 userName,前端传的是 user_name,反序列化后为 null。
✅ 解决:全局配置 spring.jackson.property-naming-strategy=SNAKE_CASE,或字段上加 @JsonProperty("user_name")。
4:未知属性导致反序列化失败
现象:前端多传了一个字段,后端反序列化直接报错。
根因:FAIL_ON_UNKNOWN_PROPERTIES 默认开启(Jackson 原生默认,SpringBoot 已默认关闭)。
✅ 解决:SpringBoot 默认已关闭,如果手动配置了 ObjectMapper,记得加上 .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)。
5:单例 ObjectMapper 不是线程安全的?
很多人以为 ObjectMapper 不是线程安全的,每次用都 new 一个,这是错误的。
✅ ObjectMapper 是线程安全的(配置完成后),应该做成单例复用。
频繁创建 ObjectMapper 会导致性能严重下降,因为它内部要做大量的序列化器缓存初始化。
六、全文总结
回到最初的问题:为什么 SpringBoot 一直坚持用 Jackson?
答案不是单一的,而是五个维度的综合考量:
-
1. 生态成熟:几乎所有 Java 框架都用 Jackson,换掉它牵一发而动全身; -
2. 架构优秀:分层清晰、Module 可扩展,能适应各种复杂场景; -
3. 性能够用:不是最快的,但足够快且稳定,真实场景中序列化很少是瓶颈; -
4. 安全可靠:安全设计谨慎,漏洞少,响应快,适合作为全球级框架的默认库; -
5. Spring 深度集成:整个 Spring 生态围绕 Jackson 构建,替换成本极高。
Jackson 不是完美的——它的 API 确实比 Gson 复杂,性能确实不是最快的,学习曲线也更陡。
但在”企业级框架默认序列化库”这个定位上,它是综合最优解。
技术选型从来不是选”最好的”,而是选”最合适的”。
对于 SpringBoot 这样的生态级框架来说,稳定性、兼容性、生态广度,比极致性能和简洁 API 重要得多。
Jackson 在这些维度上的优势,让它成为了不可替代的选择。
理解了这一点,你就不会再纠结”为什么不用 Fastjson”,而是会把精力放在用好 Jackson、发挥它的强大能力上。
JSON 序列化是每天都在用的基础能力,但很少有人深入理解它的选型逻辑和高级用法。
后续持续更新 SpringBoot 核心组件专栏:HttpMessageConverter 原理、SpringBoot 自动配置源码、数据绑定体系、类型转换机制全套干货。
喜欢 SpringBoot 原理、源码解析、后端架构内容,欢迎点赞、收藏、关注,持续跟进后端进阶开发专栏!
微信赞赏
支付宝扫码领红包

