最近这段时间用了下 RestTemplate 这个类,抽点时间总结下一些东西,希望对大家有所帮助。

从 3.0 版本开始,Spring 提供了 RestTemplate 作为用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。

本篇文章将从 RestTemplate 提供的 API 入手,先来了解下 RestTemplate 的具体使用,然后再对其中涉及到的几个核心类进行分析,最后再来分析下 RestTemplate 执行的整个流程,篇幅比较长,建议先码为快!

核心 API

在平时的使用中,我们通常都是使用包装好的getForObject/getForEntity,postForObject/postForEntity/postForLocation,put以及delete。

get 请求处理

getForEntity方法的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。

  • url:调用的服务的地址
  • responseType:返回的body类型
  • uriVariables:有两种形式:
    • 可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符
    • 也可以前面使用name={name}这种形式,最后一个参数是一个map,map的key即为前边占位符的名字,map的value为参数值

responseType 测试案例

定义的一个controller资源:

这里分别使用不同的 responseType 进行测试:

结果:

getForEntity(responseType=Map.class):{glmapper=hello glmapper}
getForEntity(responseType=String.class):{"glmapper":"hello glmapper"}
复制代码

uriVariables 测试案例

先来看下非map方式的,两个controller,两种不同方式的参数获取(本质上是一样的)

  • 使用占位符的方式:
  • 使用 map 的方式:

getForObject

getForObject 函数实际上是对 getForEntity 函数的进一步封装,如果只关注返回的消息体的内容,对其他信息都不关注,那么就可以使用 getForObject。

这里调用就比getForEntity要简单一点了,可以直接拿到对象:

getForObject 的几个重载方法和 getForEntity 基本是一样的。

post 请求处理

在RestTemplate中,POST请求可以通过如下三个方法来发起:postForEntity,postForObject,postForLocation。

postForEntity 案例

调用获取:

postForEntity(URI url, @Nullable Object request, Class<T> responseType)
复制代码
  • 方法的第一参数表示要调用的服务的地址
  • 方法的第二个参数表示上传的参数
  • 方法的第三个参数表示返回的消息体的数据类型

postForObject 案例

和 getForObject 相对应,只关注返回的消息体。

postForLocation 案例

postForLocation也是提交新资源,提交成功之后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只需要服务提供者返回一个Uri即可,该Uri表示新资源的位置。

这里有点坑,我们需要把这个uri添加到response的header中,不然后面拿到的是null。

exchange

exchange 方法和上述这些方法差别在于需要多一个请求类型的参数:

AsyncRestTemplate 异步客户端

RestTemplate的异步实现方式。所涉及到的API和RestTemplate基本一致。区别在于RestTemplate直接返回结果,而AsyncRestTemplate返回的是ListenableFuture。

RestTemplate 拦截器

Spring提供了ClientHttpRequestInterceptor和AsyncClientHttpRequestInterceptor两个接口,分别可以对RestTemplate和AsyncRestTemplate发起的请求进行拦截,并在其被发送至服务端之前修改请求或是增强相应的信息。

  • ClientHttpRequestInterceptor 拦截 RestTemplate

  • AsyncClientHttpRequestInterceptor 拦截AsyncRestTemplate

设置拦截器就是通过提供的 setInterceptors 设置即可:

自定义 ResponseErrorHandler

ResponseErrorHandler 接口定义了当response发生错误时需要进行的操作。这里我们自定义一个CustomResponseErrorHandler,当返回的code不是200时,就表示执行出错了。

设置 ResponseErrorHandler:

执行结果:

处理流程

下面来梳理下 RestTemplate 中请求处理的流程。下图中 XXXX 表示我们调用的 API 方法。大体流程就是:api 内部做一些请求相关的处理封装,然后交给 execute 方法执行,最后真正处理则是在 doExecute 方法中完成。

下面以 getForEntity 方法的执行过程来分析:

getForEntity 方法:

  • 基于给定响应类型,返回一个请求回调实现,准备请求。
  • 基于给定响应类型,返回 ResponseEntity 的响应提取器。

execute 方法:

  • 这个方法里面是对url进行urlencode编码处理的,统一转为URL。这里我们也可以手动把参数进行网络编码。

doExecute是请求真正处理的方法,这里来重点看下这个方法的执行过程:

  • createRequest
  • doWithRequest
  • execute
  • handleResponse

1、createRequest

这个方法的作用就是创建一个 ClientHttpRequest 对象。RestTemplate集成了 HttpAccessor这个抽象类,创建ClientHttpRequest的过程就是在其父类HttpAccessor中通过默认的 ClientHttpRequestFactory 实现类 SimpleClientHttpRequestFactory 完成具体的请求创建。

  • 1、创建 java.net.HttpURLConnection 对象

  • 2、设置 connection,包括 connectTimeout、setDoInput 等。

  • 3、bufferRequestBody 用于标志是否使用缓存流的形式,默认是 true。缺点是当发送大量数据时,比如 put/post,存在内存消耗严重。该值可以通过 SimpleClientHttpRequestFactory#setBufferRequestBody来修改。

不同版本的变更还是比较大的,大家在阅读源码时,还是从最新的代码来看。

2、doWithRequest

RequestCallback 封装了请求体和请求头对象。这里会遍历所有的 HttpMessageConverter,解析成所有支持的MediaType,放在allSupportedMediaTypes中。

request.getHeaders().setAccept(allSupportedMediaTypes);
复制代码

RestTemplate中对应了两个内部类的实现:

  • AcceptHeaderRequestCallback.doWithRequest的处理。 发送请求时,Http头部需要设置Accept字段,该字段表明了发送请求的这方接受的媒体类型(消息格式),也是响应端要返回的信息的媒体类型(消息格式)。 根据postForEntity方法的第三个参数responseType,程序将选择适合的解析器XXXConverter,并依据该解析器找出所有支持的媒体类型。

  • HttpEntityRequestCallback.doWithRequest的处理。 如果是POST请求并且消息体存在时,除了设置Accept字段,还可能需要设置Content-Type字段,该字段表明了所发送请求的媒体类型(消息格式),也是响应端接受的媒体类型(消息格式)。 根据postForEntity方法的第二个参数request,程序将选择适合的解析器XXXConverter,将请求消息写入输出流。

3、execute

这里会把请求头/体封装到connect,然后发送请求。跟踪 execute 方法执行,定位到SimpleBufferingClientHttpRequest#executeInternal方法:

这里是使用实例 SimpleBufferingClientHttpRequest 封装请求体和请求头。从代码中可以看到:

  • delete 时通过前面设置的 DoOutput参数和是否可以设置输出流来判断是否需要发送请求体如果是 delete 请求,那么很明显 DoOutput = false,不会有封装请求体的过程,即不执行FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream())。

4、handleResponse

最后就是 response 的解析了,从代码来看,主要还是 Error 的解析。这里的ErrorHandler我们前面也提到,可以通过实现 ResponseErrorHandler 来自定义 异常处理。

小结

本篇先介绍了RestTemplate的API使用,挑了几个介绍了下,更多使用细节还是要针对不同的场景来决定。接着对拦截器,异步RestTemplate以及错误处理器做了简单的介绍并给出了案例。最后分析了下RestTemplate的执行流程,篇幅原因执行流程部分只是大概捋了捋,其中还是很多细节有时间再补充,这部分主要就是看底层是如何通信的,已经请求参数的传递等。

聊一聊 RestTemplate相关推荐

  1. 聊一聊 软件系统中的“热力学第二定律”

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 热力学第二定律,也叫"熵增定律".这是 ...

  2. restTemplate http请求报错:no suitable HttpMessageConverter found for response type and content type

    报错信息: org.springframework.web.client.UnknownContentTypeException: Could not extract response: no sui ...

  3. SpringCloud Alibaba微服务实战(二) - Nacos服务注册与restTemplate消费

    说在前面 基础环境搭建,理论,请看上一篇,在这就不扯理论了,直接上代码. 项目结构 代码实现 第一步:在父pom的项目中引入dependencyManagement 在引入父pom之前咱们先来回顾下d ...

  4. 运行一段时间后,RestTemplate请求报400错误

    问题描述 本地调用远端接口无误,部署到服务器上调用刚开始也无误,随着时间的推移,调用次数的增加,再次调用时报 400 Bad Request 错误. 问题代码 private String sendR ...

  5. Http请求之优雅的RestTemplate

    前言 本篇博客为对RestTemplate总结 HttpURLConnection 在讲RestTemplate之前我们来看看再没有RestTemplate之前是怎么发送http请求的. privat ...

  6. java rest httpclient_java http请求建议使用webClient,少用RestTemplate,不用HttpClient

    简介: webClient:是Spring-webFlux包下的,非阻塞响应,最低java8支持函数式编程,性能好 RestTemplate:是Spring-webmvc包下的,满足RestFul原则 ...

  7. Spring Boot 中的 RestTemplate不好用?试试 Retrofit !

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 六点半起床 来源 | juejin.im/post/68 ...

  8. SpringBoot第十六篇:用restTemplate消费服务

    这篇文章主要介绍怎么用消费一个 Restful的web服务.我将用restTemplate去消费一个服务: http://gturnquist-quoters.cfapps.io/api/random ...

  9. Spring Cloud第二篇:服务消费者RestTemplate+Ribbon

    在上一篇文章,讲了服务的注册和发现.在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的.Spring cloud有两种服务调用方式,一种是ribbon+r ...

最新文章

  1. 如何将Vision Transformer应用在移动端?
  2. thinkcmf5调用指定分类的二级_Tengine快速上手系列教程amp;视频:基于Python API的图片分类应用入门丨附彩蛋...
  3. 在哪里能找到最后的版本的示例程序? AI Studio-MNIST
  4. 64.JPA命名策略【从零开始学Spring Boot】
  5. linux avd 界面,Android Studio创建AVD
  6. 北方大学 ACM 多校训练赛 第十五场 买花
  7. C#中变量(成员变量、局部变量、全局变量)的作用域
  8. [原][歌曲]感动的歌曲排序
  9. 你的飞碟在这儿(洛谷-P1200 )
  10. OpenCV中基本数据结构(6)_Matx
  11. BZOJ4278 : [ONTAK2015]Tasowanie
  12. java下linux和window通用的获取指定网段的本地ip地址(NetworkInterface)
  13. 线程打印_Java编程核心技术之——线程操作
  14. 16.进程间的通信:管道
  15. Python里Matplotlib的pyplot模块绘制简单图形小例子
  16. c++ 11/14新特性
  17. Alfred神器使用手册
  18. kfold cross_validate Stratified KFold StratifiedKFold 和 StratifiedShuffleSplit 交叉验证方法
  19. Linux系统安装教程(非双系统/虚拟机安装教程)
  20. Java创建对象方式初谈

热门文章

  1. Golang之不可重入函数实现
  2. spark eventLoop模型
  3. css定位,的重新理解,仔细理解描述即可
  4. 小tip: base64:URL背景图片与web页面性能优化(转载)
  5. 百度推广为什么出现在右侧
  6. 上传文件的加密和下载文件解密
  7. http 请求 超时时间设置
  8. 可编程控制器是计算机,可编程控制器的工作原理!与我们PC有什么区别呢?
  9. IDEA中修改自动生成的Servlet模板,提高编码效率
  10. elastic-job动态任务配置