最近这段时间用了下 RestTemplate 这个类,抽点时间总结下一些东西,但愿对你们有所帮助。java
从 3.0 版本开始,Spring 提供了 RestTemplate 做为用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,可以大大提升客户端的编写效率。api
本篇文章将从 RestTemplate 提供的 API 入手,先来了解下 RestTemplate 的具体使用,而后再对其中涉及到的几个核心类进行分析,最后再来分析下 RestTemplate 执行的整个流程,篇幅比较长,建议先码为快!缓存
在平时的使用中,咱们一般都是使用包装好的getForObject/getForEntity,postForObject/postForEntity/postForLocation,put以及delete。bash
getForEntity方法的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。网络
定义的一个controller资源:app
结果:异步
getForEntity(responseType=Map.class):{glmapper=hello glmapper}
getForEntity(responseType=String.class):{"glmapper":"hello glmapper"}
复制代码
先来看下非map方式的,两个controller,两种不一样方式的参数获取(本质上是同样的) 函数
getForObject 函数其实是对 getForEntity 函数的进一步封装,若是只关注返回的消息体的内容,对其余信息都不关注,那么就可使用 getForObject。post
这里调用就比getForEntity要简单一点了,能够直接拿到对象:测试
getForObject 的几个重载方法和 getForEntity 基本是同样的。
在RestTemplate中,POST请求能够经过以下三个方法来发起:postForEntity,postForObject,postForLocation。
postForEntity(URI url, @Nullable Object request, Class<T> responseType)
复制代码
和 getForObject 相对应,只关注返回的消息体。
postForLocation也是提交新资源,提交成功以后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只须要服务提供者返回一个Uri便可,该Uri表示新资源的位置。
这里有点坑,咱们须要把这个uri添加到response的header中,否则后面拿到的是null。
exchange 方法和上述这些方法差异在于须要多一个请求类型的参数:
RestTemplate的异步实现方式。所涉及到的API和RestTemplate基本一致。区别在于RestTemplate直接返回结果,而AsyncRestTemplate返回的是ListenableFuture。
Spring提供了ClientHttpRequestInterceptor和AsyncClientHttpRequestInterceptor两个接口,分别能够对RestTemplate和AsyncRestTemplate发起的请求进行拦截,并在其被发送至服务端以前修改请求或是加强相应的信息。
ClientHttpRequestInterceptor 拦截 RestTemplate
AsyncClientHttpRequestInterceptor 拦截AsyncRestTemplate
设置拦截器就是经过提供的 setInterceptors 设置便可:
ResponseErrorHandler 接口定义了当response发生错误时须要进行的操做。这里咱们自定义一个CustomResponseErrorHandler,当返回的code不是200时,就表示执行出错了。
设置 ResponseErrorHandler:
执行结果:
下面来梳理下 RestTemplate 中请求处理的流程。下图中 XXXX 表示咱们调用的 API 方法。大致流程就是:api 内部作一些请求相关的处理封装,而后交给 execute 方法执行,最后真正处理则是在 doExecute 方法中完成。
下面以 getForEntity 方法的执行过程来分析:
getForEntity 方法:
这个方法的做用就是建立一个 ClientHttpRequest 对象。RestTemplate集成了 HttpAccessor这个抽象类,建立ClientHttpRequest的过程就是在其父类HttpAccessor中经过默认的 ClientHttpRequestFactory 实现类 SimpleClientHttpRequestFactory 完成具体的请求建立。
一、建立 java.net.HttpURLConnection 对象
二、设置 connection,包括 connectTimeout、setDoInput 等。
三、bufferRequestBody 用于标志是否使用缓存流的形式,默认是 true。缺点是当发送大量数据时,好比 put/post,存在内存消耗严重。该值能够经过 SimpleClientHttpRequestFactory#setBufferRequestBody来修改。
不一样版本的变动仍是比较大的,你们在阅读源码时,仍是从最新的代码来看。
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,将请求消息写入输出流。
这里会把请求头/体封装到connect,而后发送请求。跟踪 execute 方法执行,定位到SimpleBufferingClientHttpRequest#executeInternal方法:
最后就是 response 的解析了,从代码来看,主要仍是 Error 的解析。这里的ErrorHandler咱们前面也提到,能够经过实现 ResponseErrorHandler 来自定义 异常处理。
本篇先介绍了RestTemplate的API使用,挑了几个介绍了下,更多使用细节仍是要针对不一样的场景来决定。接着对拦截器,异步RestTemplate以及错误处理器作了简单的介绍并给出了案例。最后分析了下RestTemplate的执行流程,篇幅缘由执行流程部分只是大概捋了捋,其中仍是不少细节有时间再补充,这部分主要就是看底层是如何通讯的,已经请求参数的传递等。