IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> RestTemplate消息转换器实现详解 -> 正文阅读

[Java知识库]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());
}

如何注入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);

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数据和处理函数

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-18 23:18:09  更:2022-06-18 23:19:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 10:42:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码