其他教程
初識 Spring Boot-Spring Boot教程深入淺出系列
Spring Boot 配置-Spring Boot教程深入淺出系列
Spring Boot Actuator 介紹-Spring Boot教程深入淺出系列
1. 概述
當使用 JSON 格式時,Spring Boot 將使用一個ObjectMapper實例來序列化響應和反序列化請求。在本教程中,我們將了解配置序列化和反序列化選項的最常見方法。
2. 默認配置
默認情況下,Spring Boot 配置將:
- 禁用MapperFeature.DEFAULT_VIEW_INCLUSION
- 禁用反序列化Feature.FAIL_ON_UNKNOWN_PROPERTIES
- 禁用SerializationFeature.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(required = false) String brand, @RequestParam(required = false) String name) { return new Coffee() .setBrand(brand) .setDate(FIXED_DATE) .setName(name); }
默認情況下,調用 GET http://lolcahost:8080/coffee?brand=Lavazza時的響應將為:
{
"name": null, "brand": "Lavazza", "date": "2020-11-16T10:21:35.974" }
我們希望排除空值並使用自定義日期格式 (dd-MM-yyyy HH:mm)。最終回復將是:
{
"brand": "Lavazza", "date": "04-11-2020 10:34" }
使用 Spring Boot 時,我們可以選擇自定義默認的ObjectMapper或覆蓋它。我們將在接下來的部分中介紹這兩個選項。
3. 自定義默認ObjectMapper
在本節中,我們將看到如何自定義Spring Boot 使用的默認ObjectMapper。
3.1. 應用程序屬性和自定義 Jackson 模塊
配置映射器的最簡單方法是通過應用程序屬性。配置的一般結構是:
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 並將其標記為@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時默認使用此構建器,並會自動選擇定義的構建器:
@Bean public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { return new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER) .serializationInclusion(JsonInclude.Include.NON_NULL); }
它將默認配置兩個選項:
- 禁用MapperFeature.DEFAULT_VIEW_INCLUSION
- 禁用反序列化Feature.FAIL_ON_UNKNOWN_PROPERTIES
根據Jackson2ObjectMapperBuilder文檔,如果它們存在於類路徑中,它還會注冊一些模塊:
- jackson-datatype-jdk8:支持其他 Java 8 類型,如Optional
- jackson-datatype-jsr310:支持 Java 8 日期和時間 API 類型
- jackson-datatype-joda:支持 Joda-Time 類型
- jackson-module-kotlin:支持 Kotlin 類和數據類
這種方法的優點是Jackson2ObjectMapperBuilder提供了一種簡單直觀的方法來構建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()); }
5. 測試配置
為了測試我們的配置,我們將使用TestRestTemplate並將對象序列化為String。通過這種方式,我們可以驗證我們的Coffee對象是在沒有空值和自定義日期格式的情況下序列化的:
@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 序列化選項的幾種方法。
我們看到了兩種不同的方法:配置默認選項或覆蓋默認配置。