1. 类结构
借助 IDEA
查看 RestTemplate
的类结构:
RestOperations
:接口,意为Rest
操作,实际上也是。常用的getForObject
、postForObject
等方法都是在这个接口里面定义的。HttpAccessor
:Http
访问器。可以设置和获取创建请求的工厂ClientHttpRequestFactory
、创建请求、设置请求的初始化器。InterceptingHttpAccessor
:HttpAccessor
的子类,增加了拦截器,可以在请求发送前做一些拦截操作。
2. 基本使用
以 SpringBoot
项目为例:
@Configuration
public class MainConfiguation {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
将 RestTemplate
注入到 IOC
容器后,在需要使用的地方 Autowried
一下 RestTemplate
,然后调用 getForObject
等方法就行了。
3. 源码分析
3.1 构造方法
点进例子中 RestTemplate
的无参构造方法:
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
......
this.uriTemplateHandler = initUriTemplateHandler();
}
构造方法就干了两件事:
添加一大堆
HttpMessageConverter
,负责转换HTTP的请求和响应。初始化 uri 模板处理器,对
uri
的拼接操作,将路径参数参数拼接到url
后面,就像下面这种:String name = "zhangsan" restTemplate.getForObject("http://xxx/{name}", String.class, name);
3.2 getForObject 源码
点进去上面的 getForObject
方法:
// RestTemplate
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
// 获取请求回调
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
// 初始化消息转换提取器
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
// 执行请求
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
return new AcceptHeaderRequestCallback(responseType);
}
AcceptHeaderRequestCallback
,看名字好像是关于请求头中与 Accept-xxx
属性相关的?先不急,接下来会讲。
接着进入 execute
方法:
// RestTemplate
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
throws RestClientException {
// uri 拼接
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
// 真正发送请求
return doExecute(expanded, method, requestCallback, responseExtractor);
}
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
..........
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) {
.......
}
........
}
可以看作是分四步执行:
创建请求
createRequest
;回调点执行
requestCallback.doWithRequest
;发送请求
request.execute()
;处理响应结果
handleResponse
。
3.2.1 createRequest
// HttpAccessor
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
}
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
3.2.1.1 请求工厂的选择
// InterceptingHttpAccessor
public ClientHttpRequestFactory getRequestFactory() {
// 配置了拦截器,使用 InterceptingClientHttpRequestFactory
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
// 没有,使用默认的 SimpleClientHttpRequestFactory
// 例子中没有配置,走这个逻辑
return super.getRequestFactory();
}
}
3.2.1.2 SimpleClientHttpRequestFactory
默认创建请求的工厂是 SimpleClientHttpRequestFactory
。工厂中可以设置请求的相关参数:
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
private static final int DEFAULT_CHUNK_SIZE = 4096;
private boolean bufferRequestBody = true;
private int chunkSize = DEFAULT_CHUNK_SIZE;
private int connectTimeout = -1;
private int readTimeout = -1;
private boolean outputStreaming = true;
.......
}
可以看到有连接超时时间、读取超时时间等,默认是 -1,也就是不超时。如果想要设置 RestTemplate
请求的超时时间等,需要改一下 RestTemplate
的初始化注入,声明创建请求的工厂并设置参数。
@Configuration
public class MainConfiguation {
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// 禁用请求体缓存
requestFactory.setBufferRequestBody(false);
// 设置连接超时时间为 5 秒
requestFactory.setConnectTimeout(5000);
// 设置读取超时时间为 5 秒
requestFactory.setReadTimeout(5000);
restTemplate.setRequestFactory(requestFactory);
return restTemplate;
}
}
3.2.1.2 SimpleClientHttpRequestFactory # createRequest
回到 getRequestFactory().createRequest(url, method)
,进入 createRequest
方法
// SimpleClientHttpRequestFactory
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);
}
}
1. openConnection & prepareConnection
protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
if (!(urlConnection instanceof HttpURLConnection)) {
throw new IllegalStateException(
"HttpURLConnection required for [" + url + "] but got: " + urlConnection);
}
return (HttpURLConnection) urlConnection;
}
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
if (this.connectTimeout >= 0) {
connection.setConnectTimeout(this.connectTimeout);
}
if (this.readTimeout >= 0) {
connection.setReadTimeout(this.readTimeout);
}
boolean mayWrite =
("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod));
connection.setDoInput(true);
connection.setInstanceFollowRedirects("GET".equals(httpMethod));
connection.setDoOutput(mayWrite);
connection.setRequestMethod(httpMethod);
}
这两个方法里面的代码就跟普通使用 URLConnection
进行 http
请求一样,RestTemplate
做了封装而已,这里不过多讲解,不熟悉的可以先学习下怎么使用 URLConnection
发送 http
请求。
3.2.1.3 initialize(request)
初始化请求。
// RestTemplate
private void initialize(ClientHttpRequest request) {
this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
}
遍历 clientHttpRequestInitializers
集合,执行每个 ClientHttpRequestInitializer
的 initialize
方法。而 clientHttpRequestInitializers
是在 RestTemplate
的爷爷里面,默认为空。
public abstract class HttpAccessor {
private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();
public void setClientHttpRequestInitializers(
List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {
if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
this.clientHttpRequestInitializers.clear();
this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
}
}
public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
return this.clientHttpRequestInitializers;
}
}
而 ClientHttpRequestInitializer
是一个接口。一般情况下,可以使用它来设置请求的头部信息、认证凭据等。
3.2.2 回调点执行 requestCallback.doWithRequest
之前贴的代码片段知道了 requestCallback
的类型是 AcceptHeaderRequestCallback
,所以直接进入这个类看它的 doWithRequest
方法,而它其实是 RestTemplate
的内部类。
对于
getForObject
来说,类型是AcceptHeaderRequestCallback
,用来设置请求头;对于
postForObject
来说,类型是HttpEntityRequestCallback
,用来设置httpEntity
;
// AcceptHeaderRequestCallback
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.responseType != null) {
List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
.filter(converter -> canReadResponse(this.responseType, converter))
.flatMap((HttpMessageConverter<?> converter) -> getSupportedMediaTypes(this.responseType, converter))
.distinct()
.sorted(MediaType.SPECIFICITY_COMPARATOR)
.collect(Collectors.toList());
if (logger.isDebugEnabled()) {
logger.debug("Accept=" + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
发送 HTTP 请求时设置请求头部中的 Accept 字段。Accept 字段表示客户端能够接受的响应内容类型。
3.2.3 request.execute()
由于之前 SimpleClientHttpRequestFactory # createRequest
创建请求时,bufferRequestBody
默认为 true
,所以当前 request
的类型是 SimpleBufferingClientHttpRequest
。
先看下 SimpleBufferingClientHttpRequest
的类结构。
execute()
方法是定义在 ClientHttpRequest
接口里面的,由抽象类 AbstractClientHttpRequest
实现。
// AbstractClientHttpRequest
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}
// 由子类 AbstractBufferingClientHttpRequest 实现
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;
// AbstractBufferingClientHttpRequest
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
.......
// 轮到 SimpleBufferingClientHttpRequest
ClientHttpResponse result = executeInternal(headers, bytes);
.......
}
// 这里才由子类 SimpleBufferingClientHttpRequest 实现
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
throws IOException;
父类 AbstractBufferingClientHttpRequest
中的 executeInternal()
方法才是由 SimpleBufferingClientHttpRequest
直接实现的。
// SimpleBufferingClientHttpRequest
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
addHeaders(this.connection, headers);
.......
this.connection.connect();
......
return new SimpleClientHttpResponse(this.connection);
}
与服务端建立连接,并返回 SimpleClientHttpResponse
。
4. 进阶使用:拦截器
前面讲过 createRequest
是有创建工厂的选择,而这个选择跟 RestTemplate
是否配置了拦截器相关。
// RestTemplate 的父类
public abstract class InterceptingHttpAccessor extends HttpAccessor {
private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
public List<ClientHttpRequestInterceptor> getInterceptors() {
return this.interceptors;
}
public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
Assert.noNullElements(interceptors, "'interceptors' must not contain null elements");
// Take getInterceptors() List as-is when passed in here
if (this.interceptors != interceptors) {
this.interceptors.clear();
this.interceptors.addAll(interceptors);
AnnotationAwareOrderComparator.sort(this.interceptors);
}
}
}
ClientHttpRequestInterceptor
是 Spring
框架提供的一个接口,它允许开发人员在发送 HTTP
请求之前或之后对请求进行拦截和处理,我们可以根据需要对请求进行修改、添加头信息、记录日志等操作。
4.1 使用示例
public class MyHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// 对请求进行拦截和处理
System.out.println("Intercepting request: " + request.getURI());
// 可以修改请求头信息
HttpHeaders headers = request.getHeaders();
headers.add("X-Custom-Header", "custom-value");
// 可以修改请求体数据
byte[] modifiedBody = modifyRequestBody(body);
// 继续执行请求
ClientHttpResponse response = execution.execute(request, modifiedBody);
// 可以对响应进行处理
System.out.println("Received response with status code: " + response.getStatusCode());
return response;
}
private byte[] modifyRequestBody(byte[] body) {
// 对请求体进行修改
// ...
return modifiedBody;
}
}
@Configuration
public class MainConfiguation {
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new MyHttpRequestInterceptor());
return restTemplate;
}
}
4.2 流程分析
配置了拦截器之后,使用的请求工厂就是 InterceptingClientHttpRequestFactory
了。
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
// 注意这里,super.getRequestFactory() 默认还是 SimpleClientHttpRequestFactory
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
......
}
跟 SimpleClientHttpRequestFactory
的 createRequest
不同,InterceptingClientHttpRequestFactory
的 createRequest
仅仅只是创建了一个请求对象,类型是 InterceptingClientHttpRequest
,并把设置的拦截器列表传递过去。
为什么它的
createRequest
不用openConnection & prepareConnection
呢? 先别急,后面有答案。
// InterceptingClientHttpRequestFactory
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
4.2.1 InterceptingClientHttpRequest # executeInternal
请求类型为 InterceptingClientHttpRequest
时,request.execute()
方法的执行逻辑和前面是类似的,不过最终是执行 InterceptingClientHttpRequest
的 executeInternal
方法。
// InterceptingClientHttpRequest
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
InterceptingClientHttpRequest.InterceptingRequestExecution requestExecution = new InterceptingClientHttpRequest.InterceptingRequestExecution();
return requestExecution.execute(this, bufferedOutput);
}
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator<ClientHttpRequestInterceptor> iterator;
public InterceptingRequestExecution() {
this.iterator = InterceptingClientHttpRequest.this.interceptors.iterator();
}
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
// 遍历拦截器列表并执行拦截器的 intercept 方法
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();
return nextInterceptor.intercept(request, body, this);
} else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = InterceptingClientHttpRequest.this.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();
}
}
}
拦截器都执行完毕后,有一行代码:ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);
可以看到最终创建请求的是 InterceptingClientHttpRequest.this.requestFactory
,而这个构造函数那里说了,默认还是 SimpleClientHttpRequestFactory
,又回到最初的分析了。看到这是不是就解答了之前的疑问呢,InterceptingClientHttpRequestFactory
创建请求不用开启连接、准备连接,因为它交给代理来做了。不得不说,这个设计真的妙!
5. ClientHttpRequestInitializer 和 ClientHttpRequestInterceptor
前面提到过 ClientHttpRequestInitializer
也能对请求做一下初始化操作,那和 ClientHttpRequestInterceptor
的区别是什么呢? 进入 ClientHttpRequestInitializer
看类注释:
有一句话:不同于 ClientHttpRequestInterceptor
,ClientHttpRequestInitializer
不需要将整个请求体读入内存就能实现自定义操作。
再来比较一下接口,拦截器的 intercept
方法除了用来遍历拦截器列表并创建请求的 ClientHttpRequestExecution
外,比 ClientHttpRequestInitializer
的 initialize
方法多了一个 body
即请求体。所以注释中说的区别就是这个了。
评论区