这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战
最近工作中的项目刚好开始接触到RestTemplate的使用,作为基本没有经验又对业务无所了解的小白,同事的业务代码都给我带来了很大压力,待到明白业务后发现,RestTemplate才是业务的核心,遂又去学习了Spring的RestTemplate,今天就记录一下自己对RestTemplate远程调用的一些理解。
上一篇文章我们学习了在Springboot中创建RESTful风格的服务接口,并使用RestTemplate进行RESTful接口的访问,算是认识了RestTemplate的常用方法,详情请戳:SpringBoot实现RESTful服务接口。
RestTemplate的远程调用实现原理
RestTemplate就是SpringBoot提供的用于访问RESTful服务的客户端模板工具类。
RestTemplate类
上次我们说到,在使用了RestTemplate时可以使用最简单有效的方式去创建一个工具类对象,例如:new RestTemplate();,那么我们这次就深入RestTemplate类的定义中去观察它是如何实现的。
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
...
public RestTemplate() {
this.messageConverters = new ArrayList();
this.errorHandler = new DefaultResponseErrorHandler();
this.headersExtractor = new RestTemplate.HeadersExtractor();
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
if (!shouldIgnoreXml) {
try {
this.messageConverters.add(new SourceHttpMessageConverter());
} catch (Error var2) {
;
}
}
}
...
}
从RestTemplate类的源码定义中可以看出:
- RestTemplate继承了
InterceptingHttpAccessor类 - RestTemplate类实现了
RestOperations接口 - RestTemplate类中的无参初始化方法中创建了很多转换器
RestOperations接口
我们首先来看一下RestOperations接口的定义:
public interface RestOperations {
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType,Object... uriVariables) throws RestClientException;
void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException;
void delete(String url, Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType, Object... uriVariables) throws RestClientException;
…
}
可以看出RestTemplate中提供的get/post/put/delete等方法都是在RestOperations接口中定义的,且这些方法都遵循RESTful风格,最后在RestTemplate类中进行了相关的逻辑实现。
InterceptingHttpAccessor抽象类
从名字可以看到该类是一个拦截器相关类,其源码定义为:
public abstract class InterceptingHttpAccessor extends HttpAccessor {
private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
private volatile ClientHttpRequestFactory interceptingRequestFactory;
...
}
我们看到在InterceptingHttpAccessor类的定义中包含两个变量定义,其中一个interceptors是分负责设置和管理请求拦截器 ClientHttpRequestInterceptor的集合,另一个则是负责获取用于创建客户端HTTP请求的工厂类 ClientHttpRequestFactory。
另外,InterceptingHttpAccessor拦截器类作为一个抽象类,其本身又继承了HttpAccessor抽象类。
HttpAccessor抽象类
HttpAccessor作为顶层的抽象类,源码定义为:
public abstract class HttpAccessor {
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
…
}
其内部定义并实例化了ClientHttpRequestFactory对象,实例化是是通过其实现类SimpleClientHttpRequestFactory创建的。
继承实现关系图
从源码定义中的逻辑中,我们不难看出之间的层级继承关系。
由图分析可以得到,RestTemplate 的类层左边继承了HttpAccessor抽象类和InterceptingHttpAccessor抽象类,以此完成了与HTTP请求相关的实现机制;而右边实现了基于RESTful风格的RestOperations接口,并实现了接口中定义的方法。RestTemplate使用了面向对象中的机制,实现接口和继承抽象类来完成两个部分功能的聚合。
RestTemlpate内部执行流程
学习了RestTemlpate类的定义,接下来我们再看一下RestTemlpate类中定义的方法是怎么实现的。 我们上次说过,RestTemlpate中有get、post、put、delete、exchange、execute等方法,而通过方法的定义可以看出所有的方法最终都是通过execute方法去完成的。
@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
...
return this.execute(url, HttpMethod.GET, requestCallback, responseExtractor, (Object[])uriVariables);
}
public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
return (HttpHeaders)nonNull(this.execute(url, HttpMethod.HEAD, (RequestCallback)null, this.headersExtractor(), (Object[])uriVariables));
}
@Nullable
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = this.httpEntityCallback(request);
HttpHeaders headers = (HttpHeaders)this.execute(url, HttpMethod.POST, requestCallback, this.headersExtractor(), uriVariables);
return headers != null ? headers.getLocation() : null;
}
因此,我们的核心就是execute方法,接下来我们主要来探究execute方法的执行流程。
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);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
可以看到,在execute方法中首先通过 UriTemplateHandler 构建了一个 URI,然后将请求过程委托给 doExecute 方法进行处理,看来这个调用还挺深的。那么我们再进入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;
Object var14;
try {
//创建请求对象
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
//远程调用
response = request.execute();
//抽取返回结果
this.handleResponse(url, method, response);
var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
} catch (IOException var12) {
String resource = url.toString();
String query = url.getRawQuery();
resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
throw new ResourceAccessException("I/O error on " + method.name() + " request for "" + resource + "": " + var12.getMessage(), var12);
} finally {
if (response != null) {
response.close();
}
}
return var14;
}
从方法流程中可以看出,整个远程调用流程中主要分为三步:
- 创建请求对象request
- 执行远程调用request.execute()
- 返回请求结果
创建请求对象
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
if (logger.isDebugEnabled()) {
logger.debug("Created " + method.name() + " request for \"" + url + "\"");
}
return request;
}
创建 ClientHttpRequest 的过程是一种典型的工厂模式应用场景,创建了一个实现 ClientHttpRequestFactory 接口的 SimpleClientHttpRequestFactory 对象,然后再通过这个对象的 createRequest 方法创建了客户端请求对象 ClientHttpRequest 并返回给上层组件进行使用。
执行请求
request 即前面创建的 SimpleBufferingClientHttpRequest 类,而execute方法定义为:
@Override
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}
返回请求结果
HTTP请求处理的最后一步就是从ClientHttpResponse中读取输入流,然后格式化为一个响应体并将其转化为业务对象。我们看一下获取返回结果的handleResponse 方法:
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
ResponseErrorHandler errorHandler = getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (logger.isDebugEnabled()) {
try {
logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + ")" +
(hasError ? "; invoking error handler" : ""));
}
catch (IOException ex) {
// ignore
}
}
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
通过getErrorHandler方法获取了一个ResponseErrorHandler,如果响应的状态码错误,可以调用handleError来处理错误并抛出异常。 而获取响应数据并完成转化的工作是在 ResponseExtractor 中,在 RestTemplate 类中,我们定义了一个 ResponseEntityResponseExtractor 内部类实现了ResponseExtractor 接口,如下代码所示:
private class ResponseEntityResponseExtractor <T> implements ResponseExtractor<ResponseEntity<T>> {
@Nullable
private final HttpMessageConverterExtractor<T> delegate;
public ResponseEntityResponseExtractor(@Nullable Type responseType) {
if (responseType != null && Void.class != responseType) {
this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
@Override
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
}
else {
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
}
}
}
代码中ResponseEntityResponseExtractor中的extractData方法本质上是将数据提取部分的工作委托给了一个代理对象delegate,这个delegate的类型就是HttpMessageConverterExtractor,HttpMessageConverterExtractor 类的内部使用了HttpMessageConverter实现消息的转换。
最后,通过RestTemplate发起、执行及响应整个HTTP请求的完整流程就介绍完毕了。