如何正确理解RestTemplate远程调用的实现原理?

本文从源码出发理解RestTemplate实现远程调用的底层原理。

初始化RestTemplate实例

我们可以通过RestTemplate所提供的几个构造函数来对其进行初始化。在分析这些构造函数之前,有必要先看一下RestTemplate类的定义,如下所示:

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations

可以看到,RestTemplate扩展了InterceptingHttpAccessor抽象类,并实现了RestOperations接口。我们围绕RestTemplate的方法定义来梳理它在设计上的思想。

首先,我们来到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;

}

显然,正是RestOperations接口定义了所有我们上一课时中介绍到的get/post/put/delete/exhange等远程调用方法组,而这些方法都是遵循RESTful架构风格而设计的。RestTemplate对这些接口都提供了实现,这是它的一条代码支线。

然后,我们再来看InterceptingHttpAccessor,它是一个抽象类,包含的核心变量如下所示:

public abstract class InterceptingHttpAccessor extends HttpAccessor {

private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();

private volatile ClientHttpRequestFactory interceptingRequestFactory;

}

通过变量定义,我们明确了InterceptingHttpAccessor应该包含两部分的处理功能,一部分是设置和管理请求拦截器ClientHttpRequestInterceptor,另一部分则是负责获取用于创建客户端HTTP请求的工厂类ClientHttpRequestFactory。

同时,我们注意到InterceptingHttpAccessor同样存在一个父类HttpAccessor,这个父类值得展开一下,因为它真正完成了ClientHttpRequestFactory 创建以及如何通过ClientHttpRequestFactory获取代表客户端请求的ClientHttpRequest对象。HttpAccessor的核心变量如下所示:

public abstract class HttpAccessor {

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

}

可以看到,HttpAccessor中创建了SimpleClientHttpRequestFactory作为系统默认的ClientHttpRequestFactory。关于ClientHttpRequestFactory,本课时后续内容中还会进行详细的讨论、

作为总结,我们来梳理一下RestTemplate的类层结构,如下所示:


从RestTemplate的类层结构中,我们可以理解它的设计思想。整个类层结构可以清晰的分成两条支线,左边部分用于完成与HTTP请求相关的实现机制,而后边部分则提供了基于RESTful风格的操作入口,并使用了面向对象中的接口和抽象类完成了这两部分功能的聚合。

介绍完RestTemplate的实例化过程,接下来我们来分析它的核心执行流程。作为用于远程调用的模板工具类,我们可以从具备多种请求方式的exchange方法入手,该方法如下所示:

@Override

public <T> ResponseEntity<T> exchange(String url, HttpMethod method,

@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)

throws RestClientException {

//构建请求回调

RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);

//构建响应体抽取器

ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);

//执行远程调用

return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));

}

显然,我们应该进一步关注这里的execute方法。事实上,无论我们采用get/put/post/delete中的哪种方法来发起请求,在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);

return doExecute(expanded, method, requestCallback, responseExtractor);

}

execute方法首先通过UriTemplateHandler构建了一个URI,然后将请求过程委托给了doExecute方法进行处理,该方法定义如下:

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 {

//创建请求对象

ClientHttpRequest request = createRequest(url, method);

if (requestCallback != null) {

//执行对请求的回调

requestCallback.doWithRequest(request);

}

//获取调用结果

response = request.execute();

//处理调用结果

handleResponse(url, method, response);

//使用结果提取从结果中提取数据

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();

}

}

}

从上述方法中,我们可以清晰地看到使用RestTemplate进行远程调用所涉及到的三大步骤,即创建请求对象、执行远程调用以及处理响应结果。让我们一起来分别来看一下。

创建请求对象

创建请求对象的入口方法如下所示:

ClientHttpRequest request = createRequest(url, method);

跟进这里的createRequest方法,我们发现流程就执行到了前面介绍的HttpAccessor类,如下所示:

public abstract class HttpAccessor {

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

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并返回给上层组件进行使用。ClientHttpRequestFactory接口的定义如下所示:

public interface ClientHttpRequestFactory {

//创建客户端请求对象

ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;

}

在Spring中,存在一批ClientHttpRequestFactory 接口的实现类,SimpleClientHttpRequestFactory是它的默认实现,开发人员也可以根据需要实现自定义的ClientHttpRequestFactory。简单起见,我们直接跟踪SimpleClientHttpRequestFactory的代码,来到它的createRequest方法,如下所示:

private boolean bufferRequestBody = true;

@Override

public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {

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);

}

}

上述createRequest中,首先我们通过传入的URI对象构建了一个HttpURLConnection对象,然后对该对象进行一些预处理,最后构造并返回一个ClientHttpRequest的实例。

通过翻阅代码,我们发现在上述openConnection方法中就是简单地通过URL对象的openConnection方法返回了一个UrlConnection。而在prepareConnection方法中,也只是完成了对HttpUrlConnection超时时间、请求方法等常见属性的设置。

注意到bufferRequestBody参数的值为true,所以通过createRequest方法最终结果是返回一个SimpleBufferingClientHttpRequest对象。

执行远程调用

一旦获取请求对象,就可以发起远程调用并获取响应了,RestTemplate中的入口方法如下所示:

response = request.execute();

这里的request就是前面创建的SimpleBufferingClientHttpRequest类,我们可以先来看一下该类的类层结构,如下图所示:


在上图的AbstractClientHttpRequest中,定义了如下所示的execute方法:

@Override

public final ClientHttpResponse execute() throws IOException {

assertNotExecuted();

ClientHttpResponse result = executeInternal(this.headers);

this.executed = true;

return result;

}

protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;

AbstractClientHttpRequest类的作用就就是防止HTTP请求的Header和Body被多次写入,所以在这个execute方法返回之前设置了executed标志位。同时,在execute方法中,最终调用了一个抽象方法executeInternal,而这个方法的实现是在AbstractClientHttpRequest的子类AbstractBufferingClientHttpRequest中,如下所示:

@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;

}

protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException;

和AbstractClientHttpRequest类一样,这里进一步梳理了一个抽象方法executeInternal,而这个抽象方法则由最底层的SimpleBufferingClientHttpRequest类来实现,如下所示:

@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);

}

这里通过FileCopyUtils.copy工具方法将结果写入到输出流上。而executeInternal方法最终返回的是一个包装了Connection对象的SimpleClientHttpResponse。

处理响应结果

一个HTTP请求处理的最后一步就是从ClientHttpResponse中读取输入流,格式化成一个响应体并将其转化为业务对象,入口代码如下所示:

//处理调用结果

handleResponse(url, method, response);

//使用结果提取从结果中提取数据

return (responseExtractor != null ? responseExtractor.extractData(response) : null);

我们先来看这里的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中,该接口定义如下所示:

public interface ResponseExtractor<T> {

@Nullable

T extractData(ClientHttpResponse response) throws IOException;

}

在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来完成消息的转换,如下所示(代码做了裁剪):

public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {

private final List<HttpMessageConverter<?>> messageConverters;

@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)) {

return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);

}

}

if (this.responseClass != null) {

if (messageConverter.canRead(this.responseClass, contentType)) {

return (T) messageConverter.read((Class) this.responseClass, responseWrapper);

}

}

}

}

}

上述方法看上去有点复杂,但核心逻辑就是遍历HttpMessageConveter列表,然后判断其是否能够读取数据,如果能就调用read方法读取数据。

最后,我们来讨论一下HttpMessageConveter中的这个read方法是如何实现的。让我们来到HttpMessageConveter接口的抽象实现类AbstractHttpMessageConverter,在它的read方法中同样定义了一个抽象方法readInternal,如下所示:

@Override

public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)  throws IOException, HttpMessageNotReadableException {

return readInternal(clazz, inputMessage);

}

protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;

在上一课时中,我们已经提到Spring提供了一系列的HttpMessageConveter来完成消息的转换,这里面最简单的应该就是StringHttpMessageConverter,该类的read方法如下所示:

@Override

protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {

Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());

return StreamUtils.copyToString(inputMessage.getBody(), charset);

}

StringHttpMessageConverter的实现过程就是从输入消息HttpInputMessage中通过getBody方法获取消息体,也就是一个ClientHttpResponse对象,然后再通过copyToString方法从该对象中读取数据,并返回字符串结果。

至此,通过RestTemplate发起、执行以及响应整个HTTP请求的完整流程就介绍完毕了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值