4.1.3 RestTemplate远程调用原理分析
在4.1.2节中,我们详细描述了如何使用RestTemplate访问HTTP端点,涉及RestTemplate初始化、发起请求以及获取响应结果等核心环节。在本节中,我们将基于这些步骤,从源码出发深入理解RestTemplate实现远程调用的底层原理。
1. 远程调用主流程
我们先来看一下RestTemplate类的定义,如代码清单4-15所示。
代码清单4-15 RestTemplate类的定义代码
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations
可以看到,RestTemplate扩展了InterceptingHttpAccessor抽象类,并实现了RestOperations接口。我们围绕RestTemplate的定义来梳理它在设计上的思想。
首先,我们来看RestOperations接口的定义,这里截取了部分核心方法,如代码清单4-16所示。
代码清单4-16 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/exchange等远程调用方法组,而这些方法都是遵循RESTful架构风格而设计的。RestTemplate对这些接口都提供了实现,这是它的一条代码支线。
然后,我们再来看InterceptingHttpAccessor,它是一个抽象类,包含的核心变量如代码清单4-17所示。
代码清单4-17 InterceptingHttpAccessor中的核心变量定义代码
public abstract class InterceptingHttpAccessor extends HttpAccessor { private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); private volatile ClientHttpRequestFactory interceptingRequestFactory; ... }
通过变量定义,我们明确了InterceptingHttpAccessor应该包含两部分处理功能,一部分是设置和管理请求拦截器ClientHttpRequestInterceptor,另一部分则是获取用于创建客户端HTTP请求的工厂类ClientHttpRequestFactory。这是RestTemplate的另一条代码支线。
同时,我们注意到InterceptingHttpAccessor同样存在一个父类HttpAccessor,这个父类值得展开讨论一下,因为它真正完成了ClientHttpRequestFactory的创建以及通过ClientHttpRequestFactory获取了代表客户端请求的ClientHttpRequest对象。HttpAccessor的核心变量如代码清单4-18所示。
代码清单4-18 HttpAccessor中的核心变量定义代码
public abstract class HttpAccessor { private ClientHttpRequestFactory requestFactory = new SimpleClientHttpReques-tFactory(); ... }
可以看到,HttpAccessor创建了SimpleClientHttpRequestFactory作为系统默认的Client-HttpRequestFactory。关于ClientHttpRequestFactory,本节还会进行详细的讨论。
作为总结,我们来梳理一下RestTemplate的基本类层结构,如图4-1所示。
图4-1 RestTemplate的类层结构
通过RestTemplate的类层结构,我们可以理解它的设计思想。整个类层结构可以清晰地分成两条线,左边部分用于完成与HTTP请求相关的实现机制,而右边部分则提供了RESTful风格的操作入口,并使用了面向对象的接口和抽象类完成了对这两部分功能的聚合。
介绍完RestTemplate的实例化过程,接下来我们来分析它的核心执行流程。对于远程调用的模板工具类,我们可以从具备多种请求方式的exchange()方法入手,该方法如代码清单4-19所示。
代码清单4-19 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 = responseEntityExtra-ctor(responseType); //执行远程调用 return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); }
显然,我们应该进一步关注这里的execute()方法。事实上,无论我们采用get/put/post/delete方法组中的哪个方法来发起请求,RestTemplate负责执行远程调用的都是这个execute()方法,该方法定义如代码清单4-20所示。
代码清单4-20 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()方法进行处理,该方法定义如代码清单4-21所示。
代码清单4-21 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) { ... } finally { if (response != null) { response.close(); } } }
从上述方法中,我们可以清晰地看到使用RestTemplate进行远程调用所涉及的三大步骤,即创建请求对象、执行远程调用以及处理响应结果。让我们一起来分别看一下。
2. 创建请求对象
创建请求对象的入口方法如代码清单4-22所示。
代码清单4-22 创建请求对象的入口方法代码
ClientHttpRequest request = createRequest(url, method);
分析这里的createRequest()方法,我们发现流程执行到了前面介绍的HttpAccessor类,如代码清单4-23所示。
代码清单4-23 HttpAccessor类实现代码
public abstract class HttpAccessor { private ClientHttpRequestFactory requestFactory = new SimpleClientHttpReques-tFactory(); ... protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest request = getRequestFactory().createRequest(url, method); ... return request; } }
创建ClientHttpRequest的过程是一种典型的工厂模式应用场景,这里直接创建了一个实现ClientHttpRequestFactory接口的SimpleClientHttpRequestFactory对象,然后再通过这个对象的createRequest()方法创建了客户端请求对象ClientHttpRequest,并返回给上层组件进行使用。ClientHttpRequestFactory接口的定义如代码清单4-24所示。
代码清单4-24 ClientHttpRequestFactory接口定义代码
public interface ClientHttpRequestFactory { //创建客户端请求对象 ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException; }
在Spring Boot中,存在一批ClientHttpRequestFactory接口的实现类,SimpleClient-HttpRequestFactory是它的默认实现,开发人员也可以根据需要实现自定义的ClientHttp-RequestFactory。简单起见,我们直接跟踪SimpleClientHttpRequestFactory的代码,来到它的createRequest()方法,如代码清单4-25所示。
代码清单4-25 SimpleClientHttpRequestFactory的createRequest()方法代码
@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的实例。
通过翻阅代码,我们发现在上述代码中只是简单地通过URL对象的openConnection()方法返回了一个UrlConnection对象。而在prepareConnection()方法中,也只是完成了对HttpUrlConnection中超时时间、请求方法等常见属性的设置。
3. 执行远程调用
一旦获取了请求对象,就可以发起远程调用并获取响应结果了,RestTemplate中的入口方法如代码清单4-26所示。
代码清单4-26 通过RestTemplate获取响应结果代码
response = request.execute();
这里的request就是前面创建的SimpleBufferingClientHttpRequest类,我们可以先来看一下该类的类层结构,如图4-2所示。
图4-2 SimpleBufferingClientHttpRequest类层结构
在图4-2的AbstractClientHttpRequest抽象类中,定义了如代码清单4-27所示的execute()方法。
代码清单4-27 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中,如代码清单4-28所示。
代码清单4-28 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; } protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException;
和AbstractClientHttpRequest类一样,这里进一步梳理了一个抽象方法executeInternal(),而这个抽象方法则由最底层的SimpleBufferingClientHttpRequest类来实现,如代码清单4-29所示。
代码清单4-29 SimpleBufferingClientHttpRequest的executeInternal ()方法代码
@Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { addHeaders(this.connection, headers); 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 { this.connection.getResponseCode(); } return new SimpleClientHttpResponse(this.connection); }
这里通过FileCopyUtils.copy()工具方法将响应结果写入到输出流上。而executeInternal()方法最终返回的是一个包装了Connection对象的SimpleClientHttpResponse。
4. 处理响应结果
一个HTTP请求处理的最后一步就是从ClientHttpResponse中读取输入流,格式化成一个响应体并将其转化为业务对象,如代码清单4-30所示。
代码清单4-30 从ClientHttpResponse中提取结果数据代码
//处理调用结果 handleResponse(url, method, response); //从结果中提取数据 return (responseExtractor != null ? responseExtractor.extractData(response) : null);
我们先来看这里的handleResponse()方法,如代码清单4-31所示。
代码清单4-31 handleResponse()方法代码
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { ResponseErrorHandler errorHandler = getErrorHandler(); boolean hasError = errorHandler.hasError(response); if (logger.isDebugEnabled()) { ... } if (hasError) { errorHandler.handleError(url, method, response); } }
这段代码实际上并没有真正处理返回的数据,而只是执行了对错误的处理。通过getErrorHandler()方法获取了一个ResponseErrorHandler,如果响应的状态码是错误的,那么就调用handleError处理错误并抛出异常。
那么,获取响应数据并完成转化的工作就应该是在ResponseExtractor中,该接口定义如代码清单4-32所示。
代码清单4-32 ResponseExtractor接口定义代码
public interface ResponseExtractor<T> { @Nullable T extractData(ClientHttpResponse response) throws IOException; }
在RestTemplate类中,定义了一个ResponseEntityResponseExtractor内部类来实现Response-Extractor接口,如代码清单4-33所示。
代码清单4-33 ResponseEntityResponseExtractor类代码
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的类型就是HttpMessageConverter-Extractor。从命名上看,我们不难联想,在HttpMessageConverterExtractor类的内部,势必使用了HttpMessageConverter来完成消息的转换。其核心逻辑就是遍历HttpMessageConveter列表,然后判断其是否能够读取数据,如果能就调用read()方法读取数据。
最后,我们来讨论一下HttpMessageConveter中的read()方法是如何实现的。让我们来看HttpMessageConveter接口的抽象实现类AbstractHttpMessageConverter,在它的read()方法中同样定义了一个抽象方法readInternal(),如代码清单4-34所示。
代码清单4-34 AbstractHttpMessageConverter的read()方法代码
@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 Boot内置了一系列的HttpMessageConveter来完成消息的转换,这里面最简单的就是StringHttpMessageConverter,该类的read()方法如代码清单4-35所示。
代码清单4-35 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对象;然后通过copy-ToString()方法从该对象中读取数据,并返回字符串结果。
至此,通过RestTemplate发起、执行以及响应整个HTTP请求的完整流程就介绍完毕了。