摘要
本篇以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