做 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. 1. 排除 Jackson 依赖;
  2. 2. 引入 Gson 依赖;
  3. 3. 配置 spring.mvc.converters.preferred-json-mapper=gson
  4. 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,常见业务对象)

JSON 库
序列化吞吐
反序列化吞吐
内存占用
Jackson
~95万/秒
~80万/秒
中等
Gson
~70万/秒
~65万/秒
中等
Fastjson
~110万/秒
~100万/秒
较低
Fastjson2
~130万/秒
~120万/秒
较低

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 的精细控制;
  • • 支持 JsonpJSON 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 的 DataBinderConversionServiceTypeConverter 等数据绑定组件,和 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 横向对比

维度
Jackson
Gson
Fastjson2
性能
良好
一般
最优
生态
最丰富
一般
国内丰富
扩展性
极强
一般
中等
安全性
有历史包袱
API 易用性
较复杂
简洁
简洁
Spring 集成
原生
需配置
需配置
文档
完善
完善
中文友好
适用场景
企业级、Spring 生态
Android、简单项目
国内高并发、对性能极致要求

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. 1. 生态成熟:几乎所有 Java 框架都用 Jackson,换掉它牵一发而动全身;
  2. 2. 架构优秀:分层清晰、Module 可扩展,能适应各种复杂场景;
  3. 3. 性能够用:不是最快的,但足够快且稳定,真实场景中序列化很少是瓶颈;
  4. 4. 安全可靠:安全设计谨慎,漏洞少,响应快,适合作为全球级框架的默认库;
  5. 5. Spring 深度集成:整个 Spring 生态围绕 Jackson 构建,替换成本极高。

Jackson 不是完美的——它的 API 确实比 Gson 复杂,性能确实不是最快的,学习曲线也更陡。
但在”企业级框架默认序列化库”这个定位上,它是综合最优解。

技术选型从来不是选”最好的”,而是选”最合适的”。
对于 SpringBoot 这样的生态级框架来说,稳定性、兼容性、生态广度,比极致性能和简洁 API 重要得多。
Jackson 在这些维度上的优势,让它成为了不可替代的选择。

理解了这一点,你就不会再纠结”为什么不用 Fastjson”,而是会把精力放在用好 Jackson、发挥它的强大能力上。


JSON 序列化是每天都在用的基础能力,但很少有人深入理解它的选型逻辑和高级用法。
后续持续更新 SpringBoot 核心组件专栏:HttpMessageConverter 原理、SpringBoot 自动配置源码、数据绑定体系、类型转换机制全套干货。
喜欢 SpringBoot 原理、源码解析、后端架构内容,欢迎点赞、收藏、关注,持续跟进后端进阶开发专栏!

扫码领红包

微信赞赏支付宝扫码领红包

发表回复

后才能评论