做 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 的精细控制;
  • • 支持 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 横向对比

维度
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 原理、源码解析、后端架构内容,欢迎点赞、收藏、关注,持续跟进后端进阶开发专栏!

扫码领红包

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

发表回复

后才能评论