所有文章

https://www.cnblogs.com/lay2017/p/11740855.html

正文

上一篇文章中,我们执行了ClientHttpRequest与服务端进行交互。并返回了一个ClientHttpResponse的实例对象。

本文将继续最后一个部分,处理请求后的响应。

同样的,我们再次回顾一下restTemplate核心逻辑代码

protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { ClientHttpResponse response = null;
try {
// 生成请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
// 设置header
requestCallback.doWithRequest(request);
}
// 执行请求,获取响应
response = request.execute();
// 处理响应
handleResponse(url, method, response);
// 获取响应体对象
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
// ... 抛出异常
}
finally {
if (response != null) {
// 关闭响应流
response.close();
}
}
}

我们的关注重点在于execute之后做了哪些事情

handleResponse

先跟进handleResponse方法

protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
ResponseErrorHandler errorHandler = getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (logger.isDebugEnabled()) {
try {
int code = response.getRawStatusCode();
HttpStatus status = HttpStatus.resolve(code);
logger.debug("Response " + (status != null ? status : code));
}
catch (IOException ex) {
// ignore
}
}
if (hasError) {
errorHandler.handleError(url, method, response);
}
}

getErrorHandler获取了一个错误处理器,如果Response的状态码是错误的,那么就调用handleError处理错误并抛出异常。

extractData生成响应对象

跟进extractData方法

public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
}
else {
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
}
}

这里主要是将状态码和相依你个对象包装成一个ResponseEntity,然后返回。

我们看看body的生成。

先看一下delegate这个代理类是怎么来的

@Nullable
private final HttpMessageConverterExtractor<T> delegate; public ResponseEntityResponseExtractor(@Nullable Type responseType) {
if (responseType != null && Void.class != responseType) {
this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}

其实就是把RestTemplate构造方法中添加的HttpMessageConverter给包装了一下

跟进delegate看看它的extractData方法吧

public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper); try {
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter;
// 判断是否能够读取
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
// 读取
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
// 判断是否能够读取
if (messageConverter.canRead(this.responseClass, contentType)) {
// 读取
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
// ...
}

核心逻辑就是遍历HttpMessageConveter,如果能够读取数据,那么就调用read方法读取数据。

那么,我们看看HttpMessageConverter的直接实现类AbstractHttpMessageConverter吧,阅读一下它的read方法

public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException { return readInternal(clazz, inputMessage);
}

再跟进readInternal方法,readInternal向下有很多实现,如

我们选择一个简单的实现看看,StringHttpMessageConverter类的readInternal方法

@Override
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}

String的转换非常简单,getBody方法将获取到ClientHttpResponse中的输入流。copyToString将从输入流中读取数据,并返回字符串结果。

打开copyToString看看

public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException {
if (in == null) {
return "";
} StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, charset);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead);
}
return out.toString();
}

到这里,我们就使用HttpMessageConverter把响应实体对象生成了。正如前面说到的,会把状态码和响应实体对象包装成ResponseEntity返回给用户。用户只需要通过get方法就可以获取statusCode或者body了。

关闭输入流

restTemplate还有最后一步操作需要去做,就是在finally块中关闭输入流

finally {
if (response != null) {
response.close();
}
}

ClientHttpResponse的close方法将包含输入流的关闭

@Override
public void close() {
try {
if (this.responseStream == null) {
getBody();
}
StreamUtils.drain(this.responseStream);
this.responseStream.close();
}
catch (Exception ex) {
// ignore
}
}

总结

到这里,restTemplate的源码解读文章就全部结束了。总的来说是一个基于Http请求响应模型的,采用了restful风格的实现方式。

restTemplate源码解析(五)处理ClientHttpResponse响应对象的更多相关文章

  1. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  2. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  3. restTemplate源码解析(目录)

    restTemplate是spring实现的,基于restful风格的http请求模板.使用restTemplate可以简化请求操作的复杂性,同时规范了代码风格.本系列文章,将根据以下目录顺序,从源码 ...

  4. iOS即时通讯之CocoaAsyncSocket源码解析五

    接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四         原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...

  5. ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  6. ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  7. Spring 源码解析之DispatcherServlet源码解析(五)

    spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...

  8. restTemplate源码解析(二)restTemplate的核心逻辑

    所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们构造了一个RestTemplate的Bean实例对象.本文将主要了解一 ...

  9. jquery源码解析:jQuery静态属性对象support详解

    jQuery.support是用功能检测的方法来检测浏览器是否支持某些功能.针对jQuery内部使用. 我们先来看一些源码: jQuery.support = (function( support ) ...

随机推荐

  1. export命令import命令

    export命令import命令 export { name1, name2, …, nameN }; export { variable1 as name1, variable2 as name2, ...

  2. 学习笔记——C++编程cin测试记录

    cin读取输入流,遇到空格会暂停,下次继续读入剩下的,+++. #include <iostream> using namespace std; int main() { cout< ...

  3. 导入GoogleClusterData到MySQL

    本篇随笔记录如何导入google-cluster-data-2011-1-2的 job_events和task_events到MySQL 1. 下载数据 download_job_events: im ...

  4. 算法习题---4.3救济金发放(UVa133)

    一:题目 (n< )个人站成一圈,逆时针编号为1~n.有两个官员,A从1开始逆时针数,B从n开始顺时针数.在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上 ...

  5. Qt编写自定义控件41-自定义环形图

    一.前言 自定义环形图控件类似于自定义饼状图控件,也是提供一个饼图区域展示占比,其实核心都是根据自动计算到的百分比绘制饼图区域.当前环形图控件模仿的是echart中的环形图控件,提供双层环形图,有一层 ...

  6. idea取消terminal控制台的显示内容大小限制

    mac /Applications/IntelliJ IDEA.app/Contents/bin/idea.properties idea.cycle.buffer.size=1024修改为不限制大小 ...

  7. Bandicam录制三大神器之一

    链接: http://pan.baidu.com/s/1bnDIUOv 密码: etge

  8. PTA 8-1 jmu-java-流、文件与正则表达式 (5 分)

    0.字节流与文件 我的代码: public static byte[] readFile(String path){ File file = new File(path); FileInputStre ...

  9. S12. Android 检查更新功能实现

    [概述] 不需要从 App Store 或者指定官网直接下载,可以通过 App 直接更新到最新版本. [流程设计] 显示当前版本信息以及版本更新日志 提供 “检查更新” 按钮,点击事件处理逻辑: 1) ...

  10. Oracle日常

    Xmltype select extractvalue(value(x), '//utext') value from table(XMLSEQUENCE(extract(v_xmlContent, ...