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

 

 

 

 

posted @ 2022-03-17 10:17  迷走神经  阅读(2448)  评论(0编辑  收藏  举报