什么是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请求方法前,我们先看一下构造函数都做了那些工具。

public RestTemplate() {
#设置各种messageConvert, 比如我们最常见的StringHttpMessageConverter。
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
} if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
} if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
} if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}
#设置UriTemplateHandler
this.uriTemplateHandler = initUriTemplateHandler();
}

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

RestTemplat#getForObject()

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

getForObject

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

execute()

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

doExecute()

@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
#1.生成请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
#2.设置header
requestCallback.doWithRequest(request);
}
#3.执行请求
response = request.execute();
#4.处理响应
handleResponse(url, method, response);
#5.返回执行结果
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}

HttpAccessor

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

package org.springframework.http.client.support;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List; import org.apache.commons.logging.Log; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpLogging;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Assert; public abstract class HttpAccessor { protected final Log logger = HttpLogging.forLogName(getClass()); private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>(); public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
this.requestFactory = requestFactory;
} public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
} public void setClientHttpRequestInitializers(
List<ClientHttpRequestInitializer> clientHttpRequestInitializers) { if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
this.clientHttpRequestInitializers.clear();
this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
}
} public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
return this.clientHttpRequestInitializers;
} #在这个方法里会创建ClientHttpRequest实例并且返回。
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
} private void initialize(ClientHttpRequest request) {
this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
}
}

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

SimpleClientHttpRequestFactory类结构图

InterceptingHttpAccessor

public abstract class InterceptingHttpAccessor extends HttpAccessor {

    #拦截器属性,可以自定义拦截器实现业务逻辑,当然玩过SpringCloud Ribbon调用,对这个属性再熟悉不过了。
private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); #这个属性要和HttpAccessor.requestFactory呼应一下。
@Nullable
private volatile ClientHttpRequestFactory interceptingRequestFactory; public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
if (this.interceptors != interceptors) {
this.interceptors.clear();
this.interceptors.addAll(interceptors);
AnnotationAwareOrderComparator.sort(this.interceptors);
}
} public List<ClientHttpRequestInterceptor> getInterceptors() {
return this.interceptors;
} @Override
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
super.setRequestFactory(requestFactory);
this.interceptingRequestFactory = null;
} @Override
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
}

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


RestTemplate#doExecute()

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

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
#再回头看看Spring对RestTemplate定义里提到的,RestTemplate的核心就是使用
#HttpURLConnection和HTTP服务器进行通信。
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name()); if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
} protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
if (!HttpURLConnection.class.isInstance(urlConnection)) {
throw new IllegalStateException("HttpURLConnection required for [" + url + "] but got: " + urlConnection);
}
return (HttpURLConnection) urlConnection;
} protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
if (this.connectTimeout >= 0) {
connection.setConnectTimeout(this.connectTimeout);
}
if (this.readTimeout >= 0) {
connection.setReadTimeout(this.readTimeout);
} connection.setDoInput(true); if ("GET".equals(httpMethod)) {
connection.setInstanceFollowRedirects(true);
}
else {
connection.setInstanceFollowRedirects(false);
} if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
} connection.setRequestMethod(httpMethod);
}

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

SimpleBufferingClientHttpRequest类结构图

SimpleStreamingClientHttpRequest类结构图

requestCallback.doWithRequest()

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

request.execute()

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

#AbstractClientHttpRequest#execute()
@Override
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}

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

AbstractBufferingClientHttpRequest#executeInternal

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
}
ClientHttpResponse result = executeInternal(headers, bytes);
this.bufferedOutput = new ByteArrayOutputStream(0);
return result;
}

SimpleBufferingClientHttpRequest#executeInternal

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
addHeaders(this.connection, headers);
// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
this.connection.connect();
if (this.connection.getDoOutput()) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
// Immediately trigger the request in a no-output scenario as well
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}

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

handleResponse

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

responseExtractor.extractData

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

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

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

@Override
@SuppressWarnings({"unchecked", "rawtypes", "resource"})
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)) {
if (logger.isDebugEnabled()) {
ResolvableType resolvableType = ResolvableType.forType(this.responseType);
logger.debug("Reading to [" + resolvableType + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
catch (IOException | HttpMessageNotReadableException ex) {
throw new RestClientException("Error while extracting response for type [" +
this.responseType + "] and content type [" + contentType + "]", ex);
} throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
"for response type [" + this.responseType + "] and content type [" + contentType + "]");
}

可以看到,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. C++语法小记---智能指针

    智能指针 用于缓解内存泄露的问题 用于替代原生指针 军规:只能指向堆空间中的对象或变量 方法 在智能指针的析构函数中调用delete 重载"->"操作符,只能重载成成员函数, ...

  2. 【C++】初次学习C++指针时的一些易混或疑惑的地方

    C++中的指针是一个比较复杂的知识概念,最近我有在学习这一方面的知识,就借此文章记录一下在学习时容易产生的混淆.本人初次发技术类的分享,可能会有纰漏,欢迎诸位指正^_^! 1.*在两种语境下的含义 先 ...

  3. 给Django Admin添加验证码和多次登录尝试限制

    Django自带的Admin很好用,但是放到生产环境总还差了点什么= = 看看admin的介绍: Django奉行Python的内置电池哲学.它自带了一系列在Web开发中用于解决常见问题或需求的额外的 ...

  4. Tomcat Script(python)

    由于刚接触 Python,所以使用Python 书写一些小的脚本,进行备忘同时分享给大家 #!/usr/bin/env python # _*_coding:utf-8_*_ # author: 'l ...

  5. sql数据管理语句

    一.数据管理 1.增加数据 INSERT INTO student VALUES(1,'张三','男',20); -- 插入所有字段.一定依次按顺序插入 -- 注意不能少或多字段值 如只需要插入部分字 ...

  6. Python 图像处理 OpenCV (15):图像轮廓

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  7. PHP ftruncate() 函数

    定义和用法 ftruncate() 函数把打开文件截断到指定的长度. 如果成功则返回 TRUE,如果失败则返回 FALSE. 语法 ftruncate(file,size) 参数 描述 file 必需 ...

  8. Ynoi专练

    为了练习分块 莫队 bitset黑科技 我会写几道Ynoi 放到这里. bitset 每一位占1bit int 每一位占 4 bitye bool占1 bitye long long 8bitye L ...

  9. json-lib无法下载

    maven无法下载json-lib 配置一下这个 <classifier>jdk15</classifier> 因为远程提供了两个

  10. html标签知识(无表单、表格)

    <meta> : 定义在head中 <hgroup></hgroup> : 标题分组标签 <br>: 换行标签 !  : 空行 <p>< ...