前言
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
RestTemplate在底层可以自由切换使用HttpClient、OkHttpClient、HttpURLConnection等作为访问远程服务的基础组件。
RestTemplate的核心思想
ClientHttpRequest
ClientHttpRequest的核心逻辑是生成一个Request对象,并执行execute方法,拿到返回结果。
HttpRequest、HttpOutputMessage两个接口,定义了Request对象必需要包含的其他内容。比如url信息、请求头、请求body等。
ClientHttpRequestFactory
为了使RestTemplate能同时支持使用HttpClient、OkHttpClient、URLConnection等组件,定义一个ClientHttpRequestFactory工厂接口是很必要的。
接口声明了,可以通过URI、HttpMethod自由构架一个ClientHttpRequest对象。
ClientHttpResponse
对于请求结果,使用了ClientHttpResponse接口定义了必需包含的返回内容。其继承了接口HttpInputMessage,HttpMessage,要求实现包含返回的header,body等。
RestTemplate的类结构图
RestTemplate继承了接口RestOperations,其父类的继承顺序是HttpAccessor及InterceptingHttpAccessor。
RestOperations
接口RestOperations中定义了我们使用RestTemplate的各个方法。各种对GET、POST、PUT、DELETE、OPTIONS方法的封装和适配,此外还有通用的exchange、execute方法。
下图展示了RestOperations的部分接口方法。
HttpAccessor
HttpAccessor主要定义了ClientHttpRequestFactory对象的get、set方法,以及默认的ClientHttpRequestFactory对象(SimpleClientHttpRequestFactory,默认使用HttpURLConnection)。
InterceptingHttpAccessor
对于服务请求,我们通常会考虑添加拦截器。RestTemplate中也不例外,定义了一个接口ClientHttpRequestInterceptor,用于做请求拦截。
InterceptingHttpAccessor的主要工作之一就是提供方法,让注入ClientHttpRequestInterceptor对象,以便在请求的过程中使用。
需要额外提醒的是,当ClientHttpRequestInterceptor的接口实现不为空时,获取ClientHttpRequestFactory对象时,会默认把原有的ClientHttpRequestFactory及ClientHttpRequestInterceptor对象列表一起封装成一个新的ClientHttpRequestFactory工厂对象(InterceptingClientHttpRequestFactory)。
RestTemplate的核心组件
RestTemplate的执行流程
在RestTemplate中调用各种post、get等方法,最终都会传递到execute方法中,而execute方法的最终执行在doExecute中。
在postForObject中,先根据请求的body对象和返回类型创建一个RequestCallback对象,之后定义一个ResponseExtractor用于对返回结果数据进行提取。
uriTemplateHandler用于对url模板中的参数进行替换。
所以最终我们发现,在doExecute方法中,其执行流程是
- 创建ClientHttpRequest对象
对象的创建通过ClientHttpRequestFactory的实现类
ClientHttpRequest request = createRequest(url, method);
- 执行RequestCallback对象方法
RequestCallback是一个接口,用于请求之前对数据做前置处理。目前在RestTemplate中有两个默认实现。AcceptHeaderRequestCallback 为请求添加支持的MediaType类型,HttpEntityRequestCallback继承至AcceptHeaderRequestCallback,HttpEntityRequestCallback还会对请求数据根据支持的HttpMessageConverter,写到输出流中。
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
- 执行ClientHttpRequest对象的execute方法
执行请求,具体的实现根据实现类的类型而定。
response = request.execute();
- 判断是否执行异常
获取到请求结果后,判断是否有异常及对异常做处理
handleResponse(url, method, response);
......
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);
}
}
- 抽取返回结果
如果没有异常,则从response中抽取返回结果。
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
RestTemplate的核心组件
通过以上分析,我们已明白RestTemplate的核心逻辑和大体结构。下面我们继续分析RestTemplate在实际使用过程中,会使用到的各个组件。
1、HttpMessageConverter
HttpMessageConverter是web开发中遇到的最基础组件之一,当我们需要对web接口的输入输出需要做自定义扩展或格式化时,都会考虑通过实现自定义的HttpMessageConverter来实现。
RestTemplate的默认构造函数里初始化了各种HttpMessageConverter。
简单理解HttpMessageConverter的功能就是,判断对对象及媒体类型是否支持可读可写,并对支持的对象实现读、写功能。
GenericHttpMessageConverter继承了HttpMessageConverter,它额外自定义了一套对读写支持的方法。
HttpMessageConverter在RestTemplate中的具体使用场景主要为对请求返回的数据进行反序列化。可以在下面的ResponseExtractor中了解到具体的使用方式。以下列出部分实现了HttpMessageConverter接口的类
2、ClientHttpRequestFactory
ClientHttpRequestFactory是一个工厂接口,定义了生成ClientHttpRequest的方法。
-
SimpleClientHttpRequestFactory实现了使用HttpURLConnection创建ClientHttpRequest,为RestTemplate的默认使用方式。
-
OkHttp3ClientHttpRequestFactory实现了使用OkHttpClient创建ClientHttpRequest
-
HttpComponentsClientHttpRequestFactory实现了使用HttpClient创建ClientHttpRequest
这些不同ClientHttpRequestFactory创建的ClientHttpRequest对象都各自定义了execute()的实现方法。
3、UriTemplateHandler
UriTemplateHandler定义了将变量填充到url模板的方法。RestTemplate中默认使用DefaultUriBuilderFactory。DefaultUriBuilderFactory的具体实现细节这里暂时不再叙述。
4、RequestCallback
RequestCallback的使用比较简单,上文分析RestTemplate执行流程时已有所说明,这里不再细述。
5、ResponseErrorHandler
ResponseErrorHandler接口主要申明了用户判断返回结构是否异常以及如何处理异常的方法。默认实现为DefaultResponseErrorHandler。DefaultResponseErrorHandler通过对返回的状态码进行判断是否请求异常,当发现异常时,根据状态码的分类,抛出是服务端异常还是客户端异常。
ResponseErrorHandler在RestTemplate中的使用。
6、ResponseExtractor
ResponseExtractor中定义了从返回结果中抽取数据的方法。最终使用为HttpMessageConverterExtractor对象。即使是使用了ResponseEntityResponseExtractor对象,其本质也是对HttpMessageConverterExtractor对象的代理。
通过观察HttpMessageConverterExtractor的extractData方法发现,其本质是通过使用HttpMessageConverter来实现数据提取的。
7、ClientHttpRequestInterceptor
通过之前的分析,我们发现ClientHttpRequestInterceptor拦截器似乎并未被使用到。那其真正的使用一定是隐藏在了其中某个细节里。
RestTemplate继承的是InterceptingHttpAccessor,当我们在RestTemplate中调用getRequestFactory方法时,实际调用的是InterceptingHttpAccessor的getRequestFactory方法。
当我们设置了ClientHttpRequestInterceptor拦截器时,实际返回的是InterceptingClientHttpRequestFactory对象,他代理了原来的ClientHttpRequestFactory,创建的是一个包含ClientHttpRequestInterceptor的InterceptingClientHttpRequest对象。
当InterceptingClientHttpRequest对象执行execute方法时,会通过父类方法,逐渐调用到InterceptingClientHttpRequest内部的executeInternal方法。
executeInternal内生成一个InterceptingRequestExecution对象,其内部的execute方法会递归调用每个ClientHttpRequestInterceptor对象,最后由被代理的ClientHttpRequestFactory重新创建一个ClientHttpRequest对象,并执行其execute方法。
//如果不能理解InterceptingRequestExecution中是如何递归调用了每个ClientHttpRequestInterceptor的。请看以下分析。
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
//获取一个ClientHttpRequestInterceptor
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
//调用ClientHttpRequestInterceptor对象的intercept方法,注意这里的第三个参数this,表示传入了当前对象。所以当ClientHttpRequestInterceptor实现类的最后方法执行一句execution.execute(request, body),就又回到了方法本身。
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
if (body.length > 0) {
if (delegate instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
}
else {
StreamUtils.copy(body, delegate.getBody());
}
}
return delegate.execute();
}
}
//以BasicAuthorizationInterceptor为例
public class BasicAuthorizationInterceptor implements ClientHttpRequestInterceptor {
private final String username;
private final String password;
/**
* Create a new interceptor which adds a BASIC authorization header
* for the given username and password.
* @param username the username to use
* @param password the password to use
*/
public BasicAuthorizationInterceptor(@Nullable String username, @Nullable String password) {
Assert.doesNotContain(username, ":", "Username must not contain a colon");
this.username = (username != null ? username : "");
this.password = (password != null ? password : "");
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
String token = Base64Utils.encodeToString(
(this.username + ":" + this.password).getBytes(StandardCharsets.UTF_8));
request.getHeaders().add("Authorization", "Basic " + token);
//执行完了当前ClientHttpRequestInterceptor的逻辑,execution指向的是InterceptingRequestExecution对象,内部包含了一个ClientHttpRequestInterceptor列表。
//再次调用会导致InterceptingRequestExecution执行下一个ClientHttpRequestInterceptor。
//按照如此逻辑,直到所有的ClientHttpRequestInterceptor执行完毕,开始执行实际ClientHttpRequest的execute方法
return execution.execute(request, body);
}
}