本文从源码出发理解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请求的完整流程就介绍完毕了。