Spring加載消息轉換器的流程以及自定義類型轉換器的優先級


目錄

一、加載轉換器的流程,通過下面詳細的時序圖可知

  

 原圖見百度雲

二、自定義轉換器的加載邏輯

1、總共有三種方式

a、@bean;

b、實現WebMvcConfigurer類的方法configureMessageConverters 或者 extendMessageConverters方法;

c、繼承WebMvcConfigurationSupport類,覆蓋方法configureMessageConverters 或者 extendMessageConverters方法;(不推薦使用,因為會導致spring、springboot默認的轉換器失效,原因下面會說)

如果b和c同時定義,只會生效c的方式;

下面講講a、b同時使用和 a、c同時使用的優先級區別

2、a 和 c同時使用

  1. @bean

  2. 繼承WebMvcConfigurationSupport類,覆蓋方法configureMessageConverters

  3. 繼承WebMvcConfigurationSupport類,覆蓋方法extendMessageConverters

如下圖:

 

 

 結論:這種方法,只會有JavaSerializationConverter1、JavaSerializationConverter2兩個轉換器在處理json請求時生效,並且JavaSerializationConverter2在JavaSerializationConverter1前面。

加載流程如下:

spring啟動時,在org.springframework.context.support.AbstractApplicationContext#refresh方法中,會初始化所有的bean,如圖:

 

 調用棧如下:

preInstantiateSingletons:737, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support)
refresh:548, AbstractApplicationContext (org.springframework.context.support)

最后會調用org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

 

 在圖①中,遍歷500多個bean,進行非延遲加載的bean創建,這里會先創建我們自定義的轉換器JavaSerializationConverter,然后會創建requestMappingHandlerAdapter

創建requestMappingHandlerAdapter的過程:

這個對象是由org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter這個bean實例化方法創建的,因為我們自定義MyConverterConfig2繼承了WebMvcConfigurationSupport對象,所以這一系列方法實際是自定義對象觸發的如圖:

 

 圖中①會給org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#messageConverters屬性賦值四個默認轉換器如下:

 

 賦值后回到上面圖②:會調用MyConverterConfig2的父類WebMvcConfigurationSupport#getMessageConverters的方法,如下:

  ①當前WebMvcConfigurationSupport#messageConverters屬性為空;

  ②執行MyConverterConfig2#configureMessageConverters方法加入一個轉換器;

  ③這時WebMvcConfigurationSupport#messageConverters屬性不為空;

  ④不會執行,也就不會記載spring默認八個轉換器;

  ⑤執行MyConverterConfig2#extendMessageConverters方法加入一個轉換器;

  方法退出后,WebMvcConfigurationSupport#messageConverters就有兩個我們自定義的轉換器:

  RequestMappingHandlerAdapter#messageConverters中也會添加返回的兩個自定義轉換器;

之后會執行RequestMappingHandlerAdapter#afterPropertiesSet初始化方法,如下圖:

 

 ①中會調用RequestMappingHandlerAdapter#getDefaultArgumentResolvers獲取一系列默認參數解析器列表:

重點關注圖中RequestResponseBodyMethodProcessor參數解析器,是對@RequestBody、@ResponseBody方法json入參和出參做處理的解析器,構造該解析器時調用了RequestMappingHandlerAdapter#getMessageConverters將之前RequestMappingHandlerAdapter中的兩個自定義解析器填充到了AbstractMessageConverterMethodArgumentResolver#messageConverters

中,也就是RequestResponseBodyMethodProcessor的父類中;

結論:從這里可以看出一旦通過這種方式配置的自定義轉換器,就會覆蓋默認的spring、springboot的各種轉換器,處理json就只會用自定義的轉換器了;

后面org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers創建異常處理器時也是執行MyConverterConfig2#getMessageConverters方法,此時屬性messageConverters已經存在了兩個自定義轉換器,所以直接返回填充到異常處理器中;

后續會繼續執行springboot 自動配置類中bean的初始化,其中就有HttpMessageConverters轉換器的初始化,如下:

 

創建過程細化流程見第一個標題畫的時序圖,這里不詳細列出,大概流程如下: 

如下圖:

圖中是創建時,獲取默認轉換器的方法

①中匿名創建了WebMvcConfigurationSupport的子類,添加了defaultMessageConverters方法,改子類沒有重寫父類任何方法;

②中調用了父類的getMessageConverters方法,如下圖:

 

 這個時候因為是匿名創建的子類,沒有給父類任何屬性賦值,也就意味着圖中:

①處父類的messageConverters是空的,所以會執行②

②是空方法;

③這時屬性還是空的;

④這里會獲取spirng的八個默認轉換器;

⑤是空方法;

所以這里返回了八個spring的默認轉換器;

  • 返回spring加載八個轉換器后,並排序,xml轉換器排到最后面;

  • spring 八個轉換器和springboot 默認三個轉換器組合,並賦值給HttpMessageConverters#converters(這個對象在springboot包中,也就是springboot的轉換器所屬對象),並沒有賦值給WebMvcConfigurationSupport#messageConverters,所以WebMvcConfigurationSupport#messageConverters(這個對象在spring包中,也就是spring中生效的還是我們自定義的兩個轉換器)中還是上面我們自定義類MyConverterConfig2中方法添加的兩個轉換器;

springboot三個自動配置轉換器如下圖:

 

 

兩個默認的和一個自定義的@bean轉換器

這里可以得出結論:

springboot 自動化配置默認的轉換器加載的時候會一起加載@bean自定義的轉換器,但是@bean自定義的轉換器會在第一個,優先級高於springboot 自動化配置默認轉換器

擴展:

自定義配置類MyConverterConfig2中兩個重寫方法為:converters.add(0,new JavaSerializationConverter1());

這是通過list的add重載方法,加入到了第一個,第一個參數可以指定list的順序,所以這樣寫會影響最終轉換器list中的順序,因為由圖可看出代碼執行順序優先級中extendMessageConverters < configureMessageConverters,但是extendMessageConverters中代碼converters.add(0,new JavaSerializationConverter1());是放到list第一個,所以extendMessageConverters后執行,反而配置的轉換器會在list的第一個;

正常一般都不會用到三種方式,只會用到一種,如果用到了extendMessageConverters或者configureMessageConverters任意一種,想讓自定義的轉換器在第一個(spring 接收請求的時候是遍歷轉換器list,一旦找到能夠處理請求類型的轉換器就不會往下找了),所以converters.add(0,new JavaSerializationConverter1());就能放到第一個,能保證優先使用自定義的轉換器

3、a 和 b同時使用

  1. @bean

  2. 實現WebMvcConfigurer類的方法configureMessageConverters

    添加到list末尾converters.add(new JavaSerializationConverter());

  3. 實現WebMvcConfigurer類的方法extendMessageConverters

    添加到list末尾converters.add(new JavaSerializationConverter());

 

 

 如下圖:

加載流程如下:

  1. 前面流程和之前一樣,只不過創建RequestMappingHandlerAdapter的對象不再是自定義的(WebMvcConfigurationSupport的子類MyConverterConfig2),而是spring自己的WebMvcConfigurationSupport對象,所以調用WebMvcConfigurationSupport#getMessageConverters

    方法邏輯變了如下:

    ①當前WebMvcConfigurationSupport#messageConverters屬性為空;

    ②執行WebMvcConfigurationSupport#configureMessageConverters方法,

    該方法會調用DelegatingWebMvcConfiguration#configureMessageConverters方法,最終會調用WebMvcConfigurerComposite#configureMessageConverters方法:

     這里會遍歷三個WebMvcConfigurer,包括我們自定義的MyConverterConfig也實現了WebMvcConfigurer接口,如下:

     先去遍歷springboot自動配置,也就是會創建HttpMessageConverters,跟之前一樣,如下圖:

    大致流程是:

    • 獲得spring八個默認轉換器,排序,xml轉換器放最后;

    • 組合springboot自動配置轉換器和@bean自定義轉換器,@bean自定義轉換器會放在第一個,一起和spring轉換器進行匹配,類型相同的放在一起,匹配不到額外的springboot轉換器會都放在轉換器列表的最前面,最終轉換器列表如下:

      ①是@bean自定義轉換器;

      ②、③都是spirngboot和spring重復的轉換器;

    • 這11個轉換器會填充HttpMessageConverters#converters只讀屬性,並返回填充到WebMvcConfigurationSupport#messageConverters屬性中

 

    然后會遍歷第二個WebMvcConfigurer,也就是我們的MyConverterConfig,就會調用我們自定義MyConverterConfig中的configureMessageConverters方法添加轉換器,由於我們方法中是converters.add(new JavaSerializationConverter1());,並不是converters.add(0, new JavaSerializationConverter1());所以轉換器JavaSerializationConverter1會加入到WebMvcConfigurationSupport#messageConverters列表最后一個。

 

 

   ③這時WebMvcConfigurationSupport#messageConverters屬性不為空,有12個轉換器;

   ④不會執行,也就不會再次加載spring默認八個轉換器;

   ⑤執行spring默認的WebMvcConfigurationSupport#extendMessageConverters方法,繼續會最終調用WebMvcConfigurerComposite#extendMessageConverters,如下圖:

 

 

 

 跟之前一樣,不過是方法名改為了extendMessageConverters,繼續遍歷WebMvcConfigurer,包括我們自定義的MyConverterConfig,如下:

 

 

 當遍歷到我們自定義類的時候MyConverterConfig#extendMessageConverters,會添加自定義轉換器JavaSerializationConverter2,這時有13個轉換器了:

 

 

 繼續遍歷第三個SpringDataWebConfiguration的時候,源碼如下:

public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    if (ClassUtils.isPresent("com.jayway.jsonpath.DocumentContext", this.context.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", this.context.getClassLoader())) {  
      // 如果存在DocumentContext和ObjectMapper類型
        ObjectMapper mapper = (ObjectMapper)getUniqueBean(ObjectMapper.class, this.context, ObjectMapper::new);
        ProjectingJackson2HttpMessageConverter converter = new ProjectingJackson2HttpMessageConverter(mapper);
        converter.setBeanFactory(this.context);
        this.forwardBeanClassLoader(converter);
      // ProjectingJackson2HttpMessageConverter加入到轉換器第一位
        converters.add(0, converter);
    }

    if (ClassUtils.isPresent("org.xmlbeam.XBProjector", this.context.getClassLoader())) {
        converters.add(0, this.xmlBeamHttpMessageConverter.orElseGet(() -> {
            return new XmlBeamHttpMessageConverter();
        }));
    }

}

從這里可以看出只要存在DocumentContext和ObjectMapper類型,就會將ProjectingJackson2HttpMessageConverter加入到轉換器第一位加入到轉換器列表第一位,而ProjectingJackson2HttpMessageConverter從創建的邏輯來看是支持json格式的,如下:

 

 

 所以目前轉換器列表第一位是ProjectingJackson2HttpMessageConverter,也就是spring默認采用的jackson的json轉換器,轉換器列表此時如下:

方法退出后,WebMvcConfigurationSupport#messageConverters就有這14個轉換器了;同時

RequestMappingHandlerAdapter#messageConverters也會賦值這14個轉換器

之后會執行RequestMappingHandlerAdapter#afterPropertiesSet初始化方法,進而會將14個轉換器賦值給

AbstractMessageConverterMethodArgumentResolver#messageConverters,這也就是真正處理json 時用到的轉換器列表,這里第一個雖然是ProjectingJackson2HttpMessageConverter(spring data提供的)但是該轉換器只能處理被ProjectedPayload注解注釋的接口類型的json對象。普通的json對象不會被該轉換器處理。

 

參考:https://segmentfault.com/a/1190000012659486

        https://segmentfault.com/a/1190000012658289

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM