所有文章

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

正文

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

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

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

  1. protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
  2. @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  3.  
  4. ClientHttpResponse response = null;
  5. try {
  6. // 生成请求
  7. ClientHttpRequest request = createRequest(url, method);
  8. if (requestCallback != null) {
  9. // 设置header
  10. requestCallback.doWithRequest(request);
  11. }
  12. // 执行请求,获取响应
  13. response = request.execute();
  14. // 处理响应
  15. handleResponse(url, method, response);
  16. // 获取响应体对象
  17. return (responseExtractor != null ? responseExtractor.extractData(response) : null);
  18. }
  19. catch (IOException ex) {
  20. // ... 抛出异常
  21. }
  22. finally {
  23. if (response != null) {
  24. // 关闭响应流
  25. response.close();
  26. }
  27. }
  28. }

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

handleResponse

先跟进handleResponse方法

  1. protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
  2. ResponseErrorHandler errorHandler = getErrorHandler();
  3. boolean hasError = errorHandler.hasError(response);
  4. if (logger.isDebugEnabled()) {
  5. try {
  6. int code = response.getRawStatusCode();
  7. HttpStatus status = HttpStatus.resolve(code);
  8. logger.debug("Response " + (status != null ? status : code));
  9. }
  10. catch (IOException ex) {
  11. // ignore
  12. }
  13. }
  14. if (hasError) {
  15. errorHandler.handleError(url, method, response);
  16. }
  17. }

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

extractData生成响应对象

跟进extractData方法

  1. public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
  2. if (this.delegate != null) {
  3. T body = this.delegate.extractData(response);
  4. return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
  5. }
  6. else {
  7. return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
  8. }
  9. }

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

我们看看body的生成。

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

  1. @Nullable
  2. private final HttpMessageConverterExtractor<T> delegate;
  3.  
  4. public ResponseEntityResponseExtractor(@Nullable Type responseType) {
  5. if (responseType != null && Void.class != responseType) {
  6. this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
  7. }
  8. else {
  9. this.delegate = null;
  10. }
  11. }

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

跟进delegate看看它的extractData方法吧

  1. public T extractData(ClientHttpResponse response) throws IOException {
  2. MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
  3. if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
  4. return null;
  5. }
  6. MediaType contentType = getContentType(responseWrapper);
  7.  
  8. try {
  9. for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
  10. if (messageConverter instanceof GenericHttpMessageConverter) {
  11. GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter;
  12. // 判断是否能够读取
  13. if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
  14. // 读取
  15. return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
  16. }
  17. }
  18. if (this.responseClass != null) {
  19. // 判断是否能够读取
  20. if (messageConverter.canRead(this.responseClass, contentType)) {
  21. // 读取
  22. return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
  23. }
  24. }
  25. }
  26. }
  27. // ...
  28. }

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

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

  1. public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
  2. throws IOException, HttpMessageNotReadableException {
  3.  
  4. return readInternal(clazz, inputMessage);
  5. }

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

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

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

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

打开copyToString看看

  1. public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException {
  2. if (in == null) {
  3. return "";
  4. }
  5.  
  6. StringBuilder out = new StringBuilder();
  7. InputStreamReader reader = new InputStreamReader(in, charset);
  8. char[] buffer = new char[BUFFER_SIZE];
  9. int bytesRead = -1;
  10. while ((bytesRead = reader.read(buffer)) != -1) {
  11. out.append(buffer, 0, bytesRead);
  12. }
  13. return out.toString();
  14. }

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

关闭输入流

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

  1. finally {
  2. if (response != null) {
  3. response.close();
  4. }
  5. }

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

  1. @Override
  2. public void close() {
  3. try {
  4. if (this.responseStream == null) {
  5. getBody();
  6. }
  7. StreamUtils.drain(this.responseStream);
  8. this.responseStream.close();
  9. }
  10. catch (Exception ex) {
  11. // ignore
  12. }
  13. }

总结

到这里,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. mysql排序自段为字符串类型问题解决

    677     000.000.000.000 2018-01-09 22:20:58 编辑 删除 锁定 199 666/777/888套餐标配     000.000.000.000 2018-01 ...

  2. nginx+lua 设置跨域

    nginx 配置: header_filter_by_lua_file cros.lua; access_by_lua ' if ngx.var.request_method == "OPT ...

  3. Ubuntu + Apache2 环境下用C编写 一个简单的cgi脚本

    我只学习过c语言,没有学习过prel,网上很多教程都是针对prel的,很少有针对c的.自己在Ubuntu下鼓捣了一下午,也总算是用c成功编写了一个helloworld的cgi,算是cgi入门的第一步. ...

  4. Inventor2018专业版软件安装激活教程

    如果你安装的是Autodesk Inventor Professional 2018,那么序列号为:666-69696969,产品密钥为:797J1, 如果你安装的是Autodesk Inventor ...

  5. 34 Flutter仿京东商城项目 用户注册 注册流程 POST发送验证码 倒计时功能 验证验证码

    加群452892873 下载对应34课文件,运行方法,建好项目,直接替换lib目录 以下列出的是本课涉及的文件. RegisterFirst.dart import 'package:flutter/ ...

  6. java的servlet执行过程是怎么样的?

    java的servlet执行过程是怎么样   答: Servlet执行过程:程序第一次访问,会调用servlet的init()方法初始化(只执行一次),每次程序执行都会根据请求调用doGet()或者d ...

  7. 在Mac 搭建robotframework 环境 遇到ride.py 打不开的方法(没试过,先记录在此)

    折腾来一下午,遇到了好多坑 坑 1.不要用pip 下载wxpython 2.不要用mac自带的python 3.不要自己下载wxpython 步骤: 1. 安装homebrew, /usr/bin/r ...

  8. JS 时间处理(GMT转换,超24小时加一天,时间差计算)

    计算天数,加小时,加分数 Date.prototype.Format = function (fmt) { // author: meizz var o = { "M+": thi ...

  9. CentOS 7部署 Ceph分布式存储架构

    一.概述 随着OpenStack日渐成为开源云计算的标准软件栈,Ceph也已经成为OpenStack的首选后端存储.Ceph是一种为优秀的性能.可靠性和可扩展性而设计的统一的.分布式文件系统. cep ...

  10. 123457123457#0#-----com.cym.YuErBaiKe02--前拼后广--育儿百科

    com.cym.YuErBaiKe02--前拼后广--育儿百科