SpringBoot 專案中配置多個 Jackson 的 ObjectMapper ,以及配置遇到的坑

DB發表於2023-01-13

問題說明

我們都知道,SpringBoot 專案中,如果引入了 Jackson 的包,哪怕不配置,SpringBoot 也會幫我們配置(JacksonAutoConfiguration)。

由於我的專案中需要使用多個不同的配置引數的 ObjectMapper,我同事直接配置了兩個@Bean,並且其中一個使用@ConditionalOnMissingBean 和@Primary 標註,但是奇怪的是,居然兩個 Bean 都注入成功了,瞬間對這塊邏輯產生了懷疑,本著探索的精神,進行分析!

  • 問題 1:自定義配置檔案中第一個注入的 Bean 和 JacksonAutoConfiguration 一模一樣,為啥還需要重新寫一遍,是否把第一個刪除了?
  • 問題 2:@ConditionalOnMissingBean 不應該是缺少該物件例項才會注入,否則不注入嘛?
@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.createXmlMapper(false).build();
    }

    @Bean("snakeCaseObjectMapper")
    public ObjectMapper snakeCaseObjectMapper() {
        ObjectMapper result = new ObjectMapper();
        result.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        result.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // java8日期日期處理
        JacksonUtil.setDateTimeSerializer(result);

        return result;
    }
}

原因排查分析

透過修改程式碼進行測試

  1. 將配置檔案第一個刪除掉,測試發現,只注入了一個名稱為 snakeCaseObjectMapper 的 bean;
  2. 註釋掉註解 @ConditionalOnMissingBean,測試發現,兩個 bean 都注入成功;
  3. 將 @ConditionalOnMissingBean 放在第二個 bean 上,測試發現,只注入了第一個 bean;
  4. 將配置的兩個 bean 調換上下順序,測試發現,只注入了第一個 bean;
  5. 將配置檔案第一個刪除掉,並將 @ConditionalOnMissingBean 放在第二個 bean 上,測試發現,只注入了第二個 bean。

問題答案:

第一個問題,雖然第一個 bean 和自動配置中的一樣,但是由於自定義配置和自動配置檔案載入順序的原因,就會產生不同的結果,所以是不能刪除的。

第二個問題,@ConditionalOnMissingBean 其實可以省略,如果呼叫兩個 bean 的上下順序,這個就注入不成功,因為同一個配置檔案中,如果沒有依賴關係,則按照上下順序進行載入的。

結論總結

  • 同一個配置檔案中,如果多個 bean 沒有依賴關係,則按照上下順序進行載入。

  • Spring Boot 的自動配置均是透過 spring.factories 來指定的,它的優先順序最低(執行時機是最晚的);透過掃描進來的(也就是專案組自定義配置類)優先順序是最高的。

  • 自動配置順序相關的三大註解 @AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder 只能作用於自動配置類,不能作用於被啟動類掃描的配置類中。

Jackson 自動裝配分析

  1. 在這個 JacksonAutoConfiguration 類裡面會生成一個 Primary 的 ObjectMapper 的 bean,注入 ObjectMapper 的 bean 時,依賴了 Jackson2ObjectMapperBuilder 的 bean;

  2. 找到注入 Jackson2ObjectMapperBuilder 的 bean,發現其依賴 List,具體操作就是把容器裡面的所有的 Jackson2ObjectMapperBuilderCustomizer 拿出來,對 Jackson2ObjectMapperBuilder 進行設定;Jackson2ObjectMapperBuilderCustomizer 介面只有一個方法,其實就是 Jackson2ObjectMapperBuilder 提供的回撥鉤子方法;

  3. 因此,如果要對 ObjectMapper 做自定義配置化操作,要麼實現 Jackson2ObjectMapperBuilderCustomizer 介面並注入這個實現的 bean,要麼直接使用 Jackson2ObjectMapperBuilder 進行配置。

參考連結:ConditionalOnMissingBean失效問題追蹤

相關文章