baeldung 2024-02-29
1. 概述
在使用 JSON 格式时,Spring Boot 会使用一个 ObjectMapper 实例来序列化响应和反序列化请求。
在本教程中,我们将探讨配置序列化与反序列化选项的最常见方法。
2. 默认配置
默认情况下,Spring Boot 的配置会禁用以下特性:
MapperFeature.DEFAULT_VIEW_INCLUSIONDeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIESSerializationFeature.WRITE_DATES_AS_TIMESTAMPS
我们先来看一个简单的示例:
客户端将向 /coffee?name=Lavazza 发送一个 GET 请求。
控制器将返回一个新的 Coffee 对象。
Spring 将使用 ObjectMapper 将我们的 POJO 序列化为 JSON。
我们将通过使用 String 和 LocalDateTime 对象来演示自定义选项:
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_INCLUSIONDeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
根据官方文档,如果类路径中存在以下模块,它还会自动注册:
jackson-datatype-jdk8:支持 Java 8 的其他类型,如Optionaljackson-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或相关组件)
根据项目需求和控制粒度,可以选择最适合的方式。