• RestTemplate消息转换器实现详解


    摘要

    本篇以fastjson消息转换器为例,详细的介绍了RestTemplate如何注入一个消息转换器,如何将入参pojo通过消息转换器转换给http请求,以及将http response stream转换为出参pojo。从源码设计的角度去分析消息转换器。

    RestTemplate简介:

    RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它遵循RESTful风格,简化了与HTTP服务器的通信,提供了HTTP RESTful模板化调用方法。默认情况下,RestTemplate依赖于标准JDK方式来建立HTTP连接,可以通过setRequestFactory方法修改为Apache HttpComponents, Netty, 或者OkHttp等类库。

    RestTemplate类图

    在这里插入图片描述

    HttpMessageConverter消息转换器

    RestTemplate主要通过HttpMessageConverter在POJO之间转换HTTP消息,RestTemplate会注册默认转换器,也可以通过setMessageConverters手动设置转换器。不同的spring版本默认注入的转换器可能会不一样,主要有jackson、gson等类型转换器,如果需要用fastjson方式进行pojo转换,需要手动设置为fastjson的Converter。

    HttpMessageConverter默认转换器

    HttpMessageConverter会默认注入ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter、SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter这几个消息转换器,然后会根据spring上下文中是否存在对应的类名来判断是否注入AtomFeedHttpMessageConverter、RssChannelHttpMessageConverter、MappingJackson2XmlHttpMessageConverter、Jaxb2RootElementHttpMessageConverter、MappingJackson2HttpMessageConverter、GsonHttpMessageConverter等消息转换器。不同的spring版本默认的http消息转换器略有差别。

    根据spring上下文注入的http消息转换器基本上是要引入相应的jar包,否则注入不了消息转换器对应的spring单例

    //字节数组 http消息转换器
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    //string http消息转换器
    this.messageConverters.add(new StringHttpMessageConverter());
    //可以读写Resource的 http消息转换器 比如读取media、file之类的
    this.messageConverters.add(new ResourceHttpMessageConverter());
    //Source http消息转换器 用于转换Source类型对象(DOMSource, SAXSource, StreamSource)
    this.messageConverters.add(new SourceHttpMessageConverter<Source>());
    //所有通用消息转换器
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
     
    //用于解析RSS文件请求与响应的消息转换器
    if (romePresent) {
        this.messageConverters.add(new AtomFeedHttpMessageConverter());
        this.messageConverters.add(new RssChannelHttpMessageConverter());
    }
     
    //Jackson 可以将java对象和xml进行相互转换
    if (jackson2XmlPresent) {
        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
    }
    //JAXB 可以将java对象与xml进行相互转换
    else if (jaxb2Present) {
        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }
    //jackson消息转换器 可以将json和Java对象进行相互转换
    if (jackson2Present) {
        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    //gson java对象和gson相互转换
    else if (gsonPresent) {
        this.messageConverters.add(new GsonHttpMessageConverter());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    如何注入fastjson的HttpMessageConverter?

    注入fastjson消息转换器首先需要引入fastjson的jar包,然后需要代码手动把FastJsonHttpMessageConverter设置到restTemplate的messageConverters列表中

    //注入fastjson参数转换器
            restTemplate = new RestTemplate();
            FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
            FastJsonConfig fastJsonConfig = new FastJsonConfig();
            fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
            fastConverter.setFastJsonConfig(fastJsonConfig);
            fastConverter.setDefaultCharset(Charset.forName("UTF-8"));
            HttpMessageConverter<?> converter = fastConverter;
            restTemplate.getMessageConverters().add(converter);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    FastJsonHttpMessageConverter类图

    在这里插入图片描述

    HttpMessageConverter在哪里生效?

    消息转换器在那里生效呢?请求入参当然是在request里面处理,响应出参肯定是在response里面处理。

    首先,我们先看RestTemplate最终执行请求doExecute()方法:

    在这里插入图片描述
    这个方法我们可以明显看到 处理请求参数的地方就在doWithRequest()方法,doWithRequest()方法调用的是org.springframework.web.client.RestTemplate.HttpEntityRequestCallback#doWithRequest;处理响应结果的就在extractData()方法,extractData()方法调用的是org.springframework.web.client.HttpMessageConverterExtractor#extractData。

    处理请求参数的过程

    分析org.springframework.web.client.RestTemplate.HttpEntityRequestCallback#doWithRequest

    我们看下面这个doWithRequest方法可以发现,如果requestbody为空时,不做特殊处理;

    如果requestbody不为空,那么我们需要遍历RestTemplate的消息转换器,

    如果消息转换器实现了GenericHttpMessageConverter接口,那么首先会调用该消息转换器消息的canWrite方法判断请求的mediaType是否支持写入request,

    如果支持,则调用该消息转换器消息write方法写入请求参数,即如果时fastjson消息转换器就会调用com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#write方法。

    如果该消息转换器消息的canWrite方法不支持当前mediaType,则进入下一个消息转换器判断。

    如果该消息转换器没有实现GenericHttpMessageConverter接口,则直接判断该消息转换器消息的canWrite方法是否支持请求的mediaType,支持则调该消息转换器消息write方法写入请求参数,不支持则进入下一个消息转换器判断。
    在这里插入图片描述

    分析com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#write方法

    FastJsonHttpMessageConverter的write方法调用了父类org.springframework.http.converter.AbstractHttpMessageConverter的write方法;
    在这里插入图片描述
    AbstractHttpMessageConverter的write方法如果请求时一个流式请求,则直接把入参写入流;如果不是流式请求,则调用writeInternal方法,AbstractHttpMessageConverter的writeInternal方法是一个没有默认实现的抽象方法,具体实现在子类中实现,也就是com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter的writeInternal方法中实现。
    在这里插入图片描述

    分析com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#writeInternal

    com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter的writeInternal方法中会以ByteArrayOutputStream的形式将入参写入ClientHttpRequest中,至此,入参处理就已经结束了。
    在这里插入图片描述

    处理响应参数的过程

    org.springframework.web.client.HttpMessageConverterExtractor#extractData

    extractData方法处理响应参数前面的步骤和doWithRequest处理入参的前面步骤很相似,

    先遍历消息转换器list,如果消息转换器实现了GenericHttpMessageConverter接口,那么首先会调用该消息转换器消息的canRead方法判断请求的mediaType是否支持写入response,

    如果支持,则调用该消息转换器消息read方法写入响应参数到pojo对象,即如果时fastjson消息转换器就会调用com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#read方法。

    如果该消息转换器消息的canRead方法不支持当前mediaType,则进入下一个消息转换器判断。

    如果该消息转换器没有实现GenericHttpMessageConverter接口,则直接判断该消息转换器消息的canRead方法是否支持请求的mediaType,支持则调该消息转换器消息read方法写入请求参数,不支持则进入下一个消息转换器判断。
    在这里插入图片描述

    分析com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter#read方法

    com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter的read方法调用了自身的readType方法。
    在这里插入图片描述
    com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter的readType方法直接将ClientHttpResponse的响应输入流转换成传入的泛型pojo对象
    在这里插入图片描述

    RestTemplate的HttpMessageConverter常见问题

    Q:为什么手动向messageConverters列表中add了fastjson消息转换器没有生效?

    A:没有生效的表现大概率是fastjson注解字段无效。不是新增了fastjson就一定会生效,消息转换器列表是从list列表中一条一条取出来的,如果前面的其他消息转换器命中了也就是canWrite、canRead了,就会使用命中了的消息转换器,如果fastjson消息转换器是list第一条那么大概率是会生效的。这里最好用setMessageConverters方法替换消息转换器列表,而不要直接往messageConverters中add fastjson的消息转换器

    Q:可以手动指定某个消息转换器生效吗?

    A:restTemplate并没有提供一个指定某个消息转换器生效的方法,但是可以通过setMessageConverters方法,用手动写入的消息转换器列表替换原有的消息转换器列表,间接的可以达到指定某种消息转换器生效的目的。

    Q:可以通过排除其他消息转换器的jar的方式来让指定消息转换器生效吗?

    A:可以,但是不建议这样做。可能带来的麻烦是排包导致其他的应用调用了被排除的包,其他功能失效

    相关名词解释:

    JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数

  • 相关阅读:
    简单入门linux【三】linux 组和权限
    软件设计师-UML基础教程
    在WSL中基于命令行打开Windows资源管理器
    【面试题】JS使用parseInt()、正则截取字符串中数字
    在conda创建的虚拟环境中安装jupyter以及使用
    文件存储解决方案-云存储阿里 OSS
    Spring Security+Spring Boot实现登录认证以及权限认证
    vcruntime140.dll重新安装方法,dll修复精灵一键修复dll问题
    Epoch、批量大小、迭代次数
    nginx [emerg] “stream“ directive is not allowed here in
  • 原文地址:https://blog.csdn.net/qq_26400953/article/details/125339428