什么是RestTemplate

Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others.

这是RestTemplate源码里对其自身的解释,从类名来看把类想设计成一个标准的模板, 简单来说就是简化Http的请求以及响应的封装,并且执行了Restful原则。如果没有RestTemplate,我们可能使用更多的还是Apache HttpClient工具。 另外从这个定义里我看到一个很重要的类HttpUrlConnection,这是RestTemplate与HTTP服务器通信的核心类。

RestTempate类结构


从类库中可以看出,这个类是一个很Spring的设计,继承抽象类InterceptingHttpAccessor,实现接口RestOperations。

RestTemplate执行流程图

开始分析之前,我们以RestTemplate#getForObject()为例,先浏览一下整个方法调用过程的执行流程图,几个关键步骤,看到其中的几个关键类,我会在后面详细说明这几个关键类。

RestTemplate构造函数

在看具体的执行http请求方法前,我们先看一下构造函数都做了那些工具。

  1. public RestTemplate() {
  2. #设置各种messageConvert, 比如我们最常见的StringHttpMessageConverter。
  3. this.messageConverters.add(new ByteArrayHttpMessageConverter());
  4. this.messageConverters.add(new StringHttpMessageConverter());
  5. this.messageConverters.add(new ResourceHttpMessageConverter(false));
  6. try {
  7. this.messageConverters.add(new SourceHttpMessageConverter<>());
  8. }
  9. catch (Error err) {
  10. // Ignore when no TransformerFactory implementation is available
  11. }
  12. this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
  13. if (romePresent) {
  14. this.messageConverters.add(new AtomFeedHttpMessageConverter());
  15. this.messageConverters.add(new RssChannelHttpMessageConverter());
  16. }
  17. if (jackson2XmlPresent) {
  18. this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
  19. }
  20. else if (jaxb2Present) {
  21. this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
  22. }
  23. if (jackson2Present) {
  24. this.messageConverters.add(new MappingJackson2HttpMessageConverter());
  25. }
  26. else if (gsonPresent) {
  27. this.messageConverters.add(new GsonHttpMessageConverter());
  28. }
  29. else if (jsonbPresent) {
  30. this.messageConverters.add(new JsonbHttpMessageConverter());
  31. }
  32. if (jackson2SmilePresent) {
  33. this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
  34. }
  35. if (jackson2CborPresent) {
  36. this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
  37. }
  38. #设置UriTemplateHandler
  39. this.uriTemplateHandler = initUriTemplateHandler();
  40. }

构造方法总结来说只做了两件事,添加HttpMessageConvert实现类,手动配置SpringMVC的时代,想必大家都知道HttpMessageConverter吧,顾名思义就是转换HTTP请求响应过程中的消息数据。第二就是初始化UriTemplateHandler,在initUriTemplateHanlder()方法中可以看到实际实例化的是DefaultUriBuilderFactory类并返回。

RestTemplat#getForObject()

我们现在开始沿着getForObject入口来分析一下执行一个HTTP Rest请求的流程到底是怎样的哈。

getForObject

  1. #RestTemplate.getForObject
  2. @Override
  3. @Nullable
  4. public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
  5. RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
  6. HttpMessageConverterExtractor<T> responseExtractor =
  7. new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
  8. #这里传入requestCallback和responseExtractor,调用execute()
  9. return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
  10. }

execute()

  1. #RestTemplate.execute
  2. @Override
  3. @Nullable
  4. public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
  5. @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
  6. URI expanded = getUriTemplateHandler().expand(url, uriVariables);
  7. #调用doExecute()
  8. return doExecute(expanded, method, requestCallback, responseExtractor);
  9. }

doExecute()

  1. @Nullable
  2. protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
  3. @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  4. Assert.notNull(url, "URI is required");
  5. Assert.notNull(method, "HttpMethod is required");
  6. ClientHttpResponse response = null;
  7. try {
  8. #1.生成请求
  9. ClientHttpRequest request = createRequest(url, method);
  10. if (requestCallback != null) {
  11. #2.设置header
  12. requestCallback.doWithRequest(request);
  13. }
  14. #3.执行请求
  15. response = request.execute();
  16. #4.处理响应
  17. handleResponse(url, method, response);
  18. #5.返回执行结果
  19. return (responseExtractor != null ? responseExtractor.extractData(response) : null);
  20. }
  21. catch (IOException ex) {
  22. String resource = url.toString();
  23. String query = url.getRawQuery();
  24. resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
  25. throw new ResourceAccessException("I/O error on " + method.name() +
  26. " request for \"" + resource + "\": " + ex.getMessage(), ex);
  27. }
  28. finally {
  29. if (response != null) {
  30. response.close();
  31. }
  32. }
  33. }

HttpAccessor

doExecute()方法的第一个步骤就是创建ClientHttpRequest, 这是一个接口,那么这个方法执行完会创建那个实现类呢? createRequest() 是父类HttpAccessor的方法,我们要先简单分析下HttpAcessor。

  1. package org.springframework.http.client.support;
  2. import java.io.IOException;
  3. import java.net.URI;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import org.apache.commons.logging.Log;
  7. import org.springframework.core.annotation.AnnotationAwareOrderComparator;
  8. import org.springframework.http.HttpLogging;
  9. import org.springframework.http.HttpMethod;
  10. import org.springframework.http.client.ClientHttpRequest;
  11. import org.springframework.http.client.ClientHttpRequestFactory;
  12. import org.springframework.http.client.ClientHttpRequestInitializer;
  13. import org.springframework.http.client.SimpleClientHttpRequestFactory;
  14. import org.springframework.util.Assert;
  15. public abstract class HttpAccessor {
  16. protected final Log logger = HttpLogging.forLogName(getClass());
  17. private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
  18. private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();
  19. public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
  20. Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
  21. this.requestFactory = requestFactory;
  22. }
  23. public ClientHttpRequestFactory getRequestFactory() {
  24. return this.requestFactory;
  25. }
  26. public void setClientHttpRequestInitializers(
  27. List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {
  28. if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
  29. this.clientHttpRequestInitializers.clear();
  30. this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
  31. AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
  32. }
  33. }
  34. public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
  35. return this.clientHttpRequestInitializers;
  36. }
  37. #在这个方法里会创建ClientHttpRequest实例并且返回。
  38. protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
  39. ClientHttpRequest request = getRequestFactory().createRequest(url, method);
  40. initialize(request);
  41. if (logger.isDebugEnabled()) {
  42. logger.debug("HTTP " + method.name() + " " + url);
  43. }
  44. return request;
  45. }
  46. private void initialize(ClientHttpRequest request) {
  47. this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
  48. }
  49. }

从HttpAccessor#createRequest()分析来看,会先调用getRequestFactory() 返回_SimpleClientHttpRequestFactory,然后调用SimpleClientHttpRequestFactory.createRequest()。但是这里我们忽略了一个环节,那就是**InterceptingHttpAccessor, **_这里我们需要再深吸一口气,再看看InterceptingHttpAccessor类。

SimpleClientHttpRequestFactory类结构图

InterceptingHttpAccessor

  1. public abstract class InterceptingHttpAccessor extends HttpAccessor {
  2. #拦截器属性,可以自定义拦截器实现业务逻辑,当然玩过SpringCloud Ribbon调用,对这个属性再熟悉不过了。
  3. private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
  4. #这个属性要和HttpAccessor.requestFactory呼应一下。
  5. @Nullable
  6. private volatile ClientHttpRequestFactory interceptingRequestFactory;
  7. public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
  8. if (this.interceptors != interceptors) {
  9. this.interceptors.clear();
  10. this.interceptors.addAll(interceptors);
  11. AnnotationAwareOrderComparator.sort(this.interceptors);
  12. }
  13. }
  14. public List<ClientHttpRequestInterceptor> getInterceptors() {
  15. return this.interceptors;
  16. }
  17. @Override
  18. public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
  19. super.setRequestFactory(requestFactory);
  20. this.interceptingRequestFactory = null;
  21. }
  22. @Override
  23. public ClientHttpRequestFactory getRequestFactory() {
  24. List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
  25. if (!CollectionUtils.isEmpty(interceptors)) {
  26. ClientHttpRequestFactory factory = this.interceptingRequestFactory;
  27. if (factory == null) {
  28. factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
  29. this.interceptingRequestFactory = factory;
  30. }
  31. return factory;
  32. }
  33. else {
  34. return super.getRequestFactory();
  35. }
  36. }
  37. }

这个类的关键在于重写了父类的getRequestFacotry(), 当前做过SpringCloud Ribbon远程调用,会对这个方法格外熟悉,Spring会判断拦截器属性_interceptors是否有值,如果有值则会继续判断,然后返回InterceptionClientHttpRequestFactory, 如果拦截器属性没有值,则调用父类HttpAccessor#getRequestFactory().


RestTemplate#doExecute()

通过分析抽象父类InterceptingHttpAccessor和HttpAccessor, 我们得出结果,如果没有拦截器,只是普通的RESTFUL调用,那么最终是调用SimpleClientHttpRequestFactory#createRequest().

  1. @Override
  2. public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
  3. #再回头看看Spring对RestTemplate定义里提到的,RestTemplate的核心就是使用
  4. #HttpURLConnection和HTTP服务器进行通信。
  5. HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
  6. prepareConnection(connection, httpMethod.name());
  7. if (this.bufferRequestBody) {
  8. return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
  9. }
  10. else {
  11. return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
  12. }
  13. }
  14. protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
  15. URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
  16. if (!HttpURLConnection.class.isInstance(urlConnection)) {
  17. throw new IllegalStateException("HttpURLConnection required for [" + url + "] but got: " + urlConnection);
  18. }
  19. return (HttpURLConnection) urlConnection;
  20. }
  21. protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
  22. if (this.connectTimeout >= 0) {
  23. connection.setConnectTimeout(this.connectTimeout);
  24. }
  25. if (this.readTimeout >= 0) {
  26. connection.setReadTimeout(this.readTimeout);
  27. }
  28. connection.setDoInput(true);
  29. if ("GET".equals(httpMethod)) {
  30. connection.setInstanceFollowRedirects(true);
  31. }
  32. else {
  33. connection.setInstanceFollowRedirects(false);
  34. }
  35. if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
  36. "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
  37. connection.setDoOutput(true);
  38. }
  39. else {
  40. connection.setDoOutput(false);
  41. }
  42. connection.setRequestMethod(httpMethod);
  43. }

从这里代码片段分析,我们可以回到上面提的问题, 在doExecute里createRequest()最终创建的实现类是SimpleBufferingClientHttpRequest。

SimpleBufferingClientHttpRequest类结构图

SimpleStreamingClientHttpRequest类结构图

requestCallback.doWithRequest()

根据自己需要实现接口RequestCallback#doWithRequest()。

request.execute()

这里也就是调用SimplezBufferingClientHttpRequest#execute()。通过分析SimplezBufferingClientHttpRequest代码,我们知道execute()定义在抽象父类AbstractClientHttpRequest里

  1. #AbstractClientHttpRequest#execute()
  2. @Override
  3. public final ClientHttpResponse execute() throws IOException {
  4. assertNotExecuted();
  5. ClientHttpResponse result = executeInternal(this.headers);
  6. this.executed = true;
  7. return result;
  8. }

分析这个类可以看出,很经典的一个设计,父类定义行为,具体实现交给子类,这里可以看出execute里继续调用executeInternal,这是一个抽象方法,交给子类实现。

AbstractBufferingClientHttpRequest#executeInternal

  1. @Override
  2. protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
  3. byte[] bytes = this.bufferedOutput.toByteArray();
  4. if (headers.getContentLength() < 0) {
  5. headers.setContentLength(bytes.length);
  6. }
  7. ClientHttpResponse result = executeInternal(headers, bytes);
  8. this.bufferedOutput = new ByteArrayOutputStream(0);
  9. return result;
  10. }

SimpleBufferingClientHttpRequest#executeInternal

  1. @Override
  2. protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
  3. addHeaders(this.connection, headers);
  4. // JDK <1.8 doesn't support getOutputStream with HTTP DELETE
  5. if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
  6. this.connection.setDoOutput(false);
  7. }
  8. if (this.connection.getDoOutput() && this.outputStreaming) {
  9. this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
  10. }
  11. this.connection.connect();
  12. if (this.connection.getDoOutput()) {
  13. FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
  14. }
  15. else {
  16. // Immediately trigger the request in a no-output scenario as well
  17. this.connection.getResponseCode();
  18. }
  19. return new SimpleClientHttpResponse(this.connection);
  20. }

至此,我们就拿到请求的返回结果Response了,封装SimpleClientHttpResponse并返回。似乎这几个类分析下来觉得他的调用流程并没有想象的那么复杂,整个设计还是很规范。

handleResponse

RestTemplate#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处理错误并抛出异常。

responseExtractor.extractData

ResponseExtractor是一个函数式接口,主要实现类有三个。

  • HeadersExtractor - RestTemplat的内部类。
  • ResponseEntityResponseExtractor - RestTemplate内部类。
  • HttpMessageConverterExtractor。

前两个都是内部类,我们主要看一下HttpMessageConverterExtractor。

  1. @Override
  2. @SuppressWarnings({"unchecked", "rawtypes", "resource"})
  3. public T extractData(ClientHttpResponse response) throws IOException {
  4. MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
  5. if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
  6. return null;
  7. }
  8. MediaType contentType = getContentType(responseWrapper);
  9. try {
  10. for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
  11. if (messageConverter instanceof GenericHttpMessageConverter) {
  12. GenericHttpMessageConverter<?> genericMessageConverter =
  13. (GenericHttpMessageConverter<?>) messageConverter;
  14. if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
  15. if (logger.isDebugEnabled()) {
  16. ResolvableType resolvableType = ResolvableType.forType(this.responseType);
  17. logger.debug("Reading to [" + resolvableType + "]");
  18. }
  19. return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
  20. }
  21. }
  22. if (this.responseClass != null) {
  23. if (messageConverter.canRead(this.responseClass, contentType)) {
  24. if (logger.isDebugEnabled()) {
  25. String className = this.responseClass.getName();
  26. logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
  27. }
  28. return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
  29. }
  30. }
  31. }
  32. }
  33. catch (IOException | HttpMessageNotReadableException ex) {
  34. throw new RestClientException("Error while extracting response for type [" +
  35. this.responseType + "] and content type [" + contentType + "]", ex);
  36. }
  37. throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
  38. "for response type [" + this.responseType + "] and content type [" + contentType + "]");
  39. }

可以看到,extractData先将response交给responseWrapper,如果responseWrapper有消息体且非空,则进行返回消息的读取操作。
消息的读取需要借助HttpMessageConverter接口,HttpMessageConverter具有多种实现类,以完成不同格式消息的读取,相当于消息解码器或转换头。

总结

RestTemplate提供了多种便捷访问HTTP服务的方法,提高了客户端的编码效率。底层通过使用java.net包创建HTTP请求。主要使用ClientHttpRequestFactory指定不同的HTTP请求方式。

Spring - RestTemplate执行原理分析的更多相关文章

  1. Spring依赖注入原理分析

    在分析原理之前我们先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色( ...

  2. (4.1)Spring MVC执行原理和基于Java的配置过程

    一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...

  3. spring Mvc 执行原理 及 xml注解配置说明 (六)

    Spring MVC 执行原理 在 Spring Mvc 访问过程里,每个请求都首先经过 许多的过滤器,经 DispatcherServlet 处理; 一个Spring MVC工程里,可以配置多个的 ...

  4. Spring MVC执行原理和基于Java的配置过程

    一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...

  5. Spring MVC执行原理

    spring的MVC执行原理 1.spring mvc将所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求 进行真正的处理工作. 2.DispatcherSer ...

  6. Spring Boot 启动原理分析

    https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...

  7. Spring + Mybatis 集成原理分析

    由于我之前是写在wizNote上的,迁移过来比较浪费时间,所以,这里我直接贴个图片,PDF文件我上传到百度云盘了,需要的可直接下载. 地址:https://pan.baidu.com/s/12ZJmw ...

  8. Spring Aop技术原理分析

    本篇文章从Aop xml元素的解析开始,分析了Aop在Spring中所使用到的技术.包括Aop各元素在容器中的表示方式.Aop自动代理的技术.代理对象的生成及Aop拦截链的调用等等.将这些技术串联起来 ...

  9. spring之mvc原理分析及简单模拟实现

    在之前的一篇博客中已经简单的实现了spring的IOC和DI功能,本文将在之前的基础上实现mvc功能. 一 什么是MVC MVC简单的说就是一种软件实现的设计模式,将整个系统进行分层,M(model ...

随机推荐

  1. Shell基本语法---shell数组

    shell数组 arr=( ) #定义数组 echo ${#arr[*]} #打印数组长度 echo ${arr[]} #打印数组的第一个成员 echo ${arr[]} #打印数组的二个成员 ech ...

  2. java计算下一个整5分钟时间点

    需求背景 我的需求是获取当前时间之后的下一个"整5分钟时间点". 首先,那么何为"整5分钟时间点"? 满足以下两个条件的时间: 分钟数等于以下时间中的一个,且秒 ...

  3. 【线性表基础】基于线性表的简单算法【Java版】

    本文描述了基于线性表的简单算法及其代码[Java实现] 1-1 删除单链表中所有重复元素 // Example 1-1 删除单链表中所有重复元素 private static void removeR ...

  4. Pandas 复习

    1.导包 import pandas as pd 2.数据读取,文件在该代码文件夹内 food_info = pd.read_csv('food_info.csv') 3.查看类型 food_info ...

  5. activiti7 导出bpmn文件

    最近在学习springboot+activiti7整合,想做一个导出bpmn文件的功能,查了相关资料,最后没有实现.最后查看了一下代码 找到了方法 如下所示 @GetMapping("exp ...

  6. Flask+微信公众号开发(接入指南)

    目录 一.注册公众号 二.启用开发者 三.配置服务器配置 四.开发自己的需求 五.写在最后 一.注册公众号 具体的注册过程,根据官方文档一步一步来即可.这里需注意的是订阅号还是服务号:有些比较好的开发 ...

  7. 将BX中的数以二进制形式在屏幕上显示出来。

    问题 将BX中的数以二进制形式在屏幕上显示出来. 代码 code segment assume cs:code main proc far start: mov bx,011001100110b ;假 ...

  8. PHP current() 函数

    实例 输出数组中的当前元素的值: <?php$people = array("Peter", "Joe", "Glenn", &quo ...

  9. PHP unset() 函数

    unset() 函数用于销毁给定的变量.高佣联盟 www.cgewang.com PHP 版本要求: PHP 4, PHP 5, PHP 7 语法 void unset ( mixed $var [, ...

  10. Docker这些none:none的镜像,难道就不配拥有名字吗

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! 搞容器开发一段时间后,想看看都有哪些镜像,执行了一下docker images -a,蒙圈了,有一堆<none> ...