飞道的博客

RestTemplate消息转换器实现详解

401人阅读  评论(0)

摘要

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


转载:https://blog.csdn.net/qq_26400953/article/details/125339428
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场