Spring Boot:自定义 Jackson ObjectMapper

更新于 2025-12-30

baeldung 2024-02-29

1. 概述

在使用 JSON 格式时,Spring Boot 会使用一个 ObjectMapper 实例来序列化响应和反序列化请求。

在本教程中,我们将探讨配置序列化与反序列化选项的最常见方法。


2. 默认配置

默认情况下,Spring Boot 的配置会禁用以下特性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

我们先来看一个简单的示例:

客户端将向 /coffee?name=Lavazza 发送一个 GET 请求。
控制器将返回一个新的 Coffee 对象。
Spring 将使用 ObjectMapper 将我们的 POJO 序列化为 JSON。

我们将通过使用 StringLocalDateTime 对象来演示自定义选项:

public class Coffee {

    private String name;
    private String brand;
    private LocalDateTime date;

    // getters and setters
}

同时,我们定义一个简单的 REST 控制器用于演示序列化:

@GetMapping("/coffee")
public Coffee getCoffee(@RequestParam(name = "brand", required = false) String brand,
                        @RequestParam(name = "name", required = false) String name) {
    return new Coffee()
      .setBrand(brand)
      .setDate(FIXED_DATE)
      .setName(name);
}

默认情况下,调用 GET http://localhost:8080/coffee?brand=Lavazza 将返回如下响应:

{
  "name": null,
  "brand": "Lavazza",
  "date": "2020-11-16T10:21:35.974"
}

我们希望排除空值(null),并使用自定义日期格式dd-MM-yyyy HH:mm)。最终期望的响应如下:

{
  "brand": "Lavazza",
  "date": "04-11-2020 10:34"
}

在 Spring Boot 中,我们可以选择自定义默认的 ObjectMapper,或者完全覆盖它。接下来的章节将分别介绍这两种方式。


3. 自定义默认的 ObjectMapper

本节将介绍如何对 Spring Boot 使用的默认 ObjectMapper 进行自定义。

3.1. 应用属性与自定义 Jackson 模块

最简单的方式是通过 application.properties 配置文件进行设置。

通用结构如下:

spring.jackson.<category_name>.<feature_name>=true,false

例如,要禁用 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,可以添加:

spring.jackson.serialization.write-dates-as-timestamps=false

除了上述特性分类外,还可以配置属性包含策略:

spring.jackson.default-property-inclusion=always, non_null, non_absent, non_default, non_empty

这种方式最简单,但缺点是无法实现高级定制,比如为 LocalDateTime 设置自定义日期格式。

此时,我们会得到如下结果:

{
  "brand": "Lavazza",
  "date": "2020-11-16T10:35:34.593"
}

为了实现目标,我们需要注册一个带有自定义日期格式的 JavaTimeModule

@Configuration
@PropertySource("classpath:coffee.properties")
public class CoffeeRegisterModuleConfig {

    @Bean
    public Module javaTimeModule() {
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(LOCAL_DATETIME_SERIALIZER);
        return module;
    }
}

对应的 coffee.properties 文件内容如下:

spring.jackson.default-property-inclusion=non_null

Spring Boot 会自动注册所有类型为 com.fasterxml.jackson.databind.Module 的 Bean。最终结果如下:

{
  "brand": "Lavazza",
  "date": "16-11-2020 10:43"
}

3.2. 使用 Jackson2ObjectMapperBuilderCustomizer

该函数式接口允许我们创建配置 Bean,这些配置会被应用到通过 Jackson2ObjectMapperBuilder 创建的默认 ObjectMapper 上:

@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
      .serializers(LOCAL_DATETIME_SERIALIZER);
}

这些配置 Bean 会按照特定顺序应用,可通过 @Order 注解控制顺序。这种方式优雅且适用于从多个配置或模块中组合 ObjectMapper 配置。


4. 覆盖默认配置

如果我们希望完全掌控配置,有几种方式可以禁用自动配置,仅使用我们自己的定制配置。

4.1. 直接定义 ObjectMapper Bean

最直接的方法是定义一个 ObjectMapper Bean 并标记为 @Primary

@Bean
@Primary
public ObjectMapper objectMapper() {
    JavaTimeModule module = new JavaTimeModule();
    module.addSerializer(LOCAL_DATETIME_SERIALIZER);
    return new ObjectMapper()
      .setSerializationInclusion(JsonInclude.Include.NON_NULL)
      .registerModule(module);
}

当你希望完全控制序列化过程,并且不希望受外部配置影响时,应使用此方法。

4.2. 使用 Jackson2ObjectMapperBuilder

另一种清晰的方式是定义一个 Jackson2ObjectMapperBuilder Bean。

Spring Boot 在构建 ObjectMapper 时默认就使用这个 Builder,并会自动使用你定义的实例:

@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
    return new Jackson2ObjectMapperBuilder()
      .serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
}

该 Builder 默认会禁用以下两个特性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES

根据官方文档,如果类路径中存在以下模块,它还会自动注册:

  • jackson-datatype-jdk8:支持 Java 8 的其他类型,如 Optional
  • jackson-datatype-jsr310:支持 Java 8 日期时间 API(如 LocalDateTime
  • jackson-datatype-joda:支持 Joda-Time 类型
  • jackson-module-kotlin:支持 Kotlin 类和数据类

这种方法的优点是提供了简洁直观的方式来构建 ObjectMapper

4.3. 自定义 MappingJackson2HttpMessageConverter

只需定义一个 MappingJackson2HttpMessageConverter 类型的 Bean,Spring Boot 就会自动使用它:

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
      .serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
    return new MappingJackson2HttpMessageConverter(builder.build());
}

建议阅读我们的《Spring HTTP 消息转换器》文章以了解更多相关内容。


5. 测试配置

为了验证配置是否生效,我们可以使用 TestRestTemplate,并将对象序列化为字符串进行断言。

这样可以确保 Coffee 对象被正确地序列化——不含 null 值,且使用了自定义日期格式:

@Test
public void whenGetCoffee_thenSerializedWithDateAndNonNull() {
    String formattedDate = DateTimeFormatter.ofPattern(CoffeeConstants.dateTimeFormat).format(FIXED_DATE);
    String brand = "Lavazza";
    String url = "/coffee?brand=" + brand;
    
    String response = restTemplate.getForObject(url, String.class);
    
    assertThat(response).isEqualTo("{\"brand\":\"" + brand + "\",\"date\":\"" + formattedDate + "\"}");
}

6. 结论

本文介绍了在 Spring Boot 中配置 JSON 序列化选项的多种方法。

我们探讨了两种主要思路:

  • 自定义默认配置(保留 Spring Boot 自动配置的基础上进行增强)
  • 完全覆盖默认配置(提供自己的 ObjectMapper 或相关组件)

根据项目需求和控制粒度,可以选择最适合的方式。