本文已参与「新人创作礼」活动,一起开启掘金创作之路。
RestTemplate:本质上是整理参数,调用doExecute函数。
doExecute函数:
1. 创建请求(ClientHttpRequest)
2. 请求执行(ClientHttpRequest.execute())
3. 处理响应(ClientHttpResponse)
InterceptingClientHttpRequest
AbstractClientHttpRequest
提供文件头属性,使HttpMessage.getHeaders()方法具备真正的意义。
抽象模版方法
HttpInputMessage.getBody() 和 ClientHttpRequest.execute() 方法根据抽象模版方法,分别提供内部抽象方法,供子类继承实现。
子类实现支持的getBodyInternal()和executeInternal()方法有this.headers属性加持。
AbstractBufferingClientHttpRequest
ByteArrayOutputStream bufferedOutput:构造ClientHttpRequest之后,通过RequestCallback.doWithRequest(request)方法,将请求体放入bufferedOutput中。
抽象模版方法
AbstractBufferingClientHttpRequest.executeInternal() 方法 将 【bufferedOutput】 转换成【byte[] bytes】调用内部抽象方法【executeInternal(HttpHeaders headers, byte[] bufferedOutput)】
子类实现支持的【executeInternal(HttpHeaders headers, byte[] bufferedOutput)】方法可以直接处理请求体的字节数组【byte[] bufferedOutput】
// AbstractBufferingClientHttpRequest.java
package org.springframework.http.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.springframework.http.HttpHeaders;
/**
* Base implementation of {@link ClientHttpRequest} that buffers output
* in a byte array before sending it over the wire.
*
* @author Arjen Poutsma
* @since 3.0.6
*/
abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream(1024);
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
return this.bufferedOutput;
}
@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;
}
/**
* Abstract template method that writes the given headers and content to the HTTP request.
* @param headers the HTTP headers
* @param bufferedOutput the body content
* @return the response object for the executed request
*/
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
throws IOException;
}
InterceptingClientHttpRequest
增加 拦截器集合List<ClientHttpRequestInterceptor> interceptors
ClientHttpRequestExecution
ClientHttpRequestExecution提供execute方法,执行带有请求体的Http报文请求。 如果存在拦截器,执行拦截器方法,如果没有拦截器,执行ClientHttpRequest.execute()方法。
ClientHttpRequestInterceptor提供intercept方法,执行拦截器逻辑,然后继续调用ClientHttpRequestExecution.execute方法。
// ClientHttpRequestExecution.java
package org.springframework.http.client;
import java.io.IOException;
import org.springframework.http.HttpRequest;
/**
* Represents the context of a client-side HTTP request execution.
*
* <p>Used to invoke the next interceptor in the interceptor chain,
* or - if the calling interceptor is last - execute the request itself.
*
* @author Arjen Poutsma
* @since 3.1
* @see ClientHttpRequestInterceptor
*/
@FunctionalInterface
public interface ClientHttpRequestExecution {
/**
* Execute the request with the given request attributes and body,
* and return the response.
* @param request the request, containing method, URI, and headers
* @param body the body of the request to execute
* @return the response
* @throws IOException in case of I/O errors
*/
ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException;
}
InterceptingRequestExecution
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator<ClientHttpRequestInterceptor> iterator;
public InterceptingRequestExecution() {
this.iterator = interceptors.iterator();
}
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
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();
}
}
}
package org.springframework.http.client;
import java.io.IOException;
import org.springframework.http.HttpRequest;
/**
* Intercepts client-side HTTP requests. Implementations of this interface can be
* {@linkplain org.springframework.web.client.RestTemplate#setInterceptors registered}
* with the {@link org.springframework.web.client.RestTemplate RestTemplate},
* as to modify the outgoing {@link ClientHttpRequest} and/or the incoming
* {@link ClientHttpResponse}.
*
* <p>The main entry point for interceptors is
* {@link #intercept(HttpRequest, byte[], ClientHttpRequestExecution)}.
*
* @author Arjen Poutsma
* @since 3.1
*/
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
/**
* Intercept the given request, and return a response. The given
* {@link ClientHttpRequestExecution} allows the interceptor to pass on the
* request and response to the next entity in the chain.
* <p>A typical implementation of this method would follow the following pattern:
* <ol>
* <li>Examine the {@linkplain HttpRequest request} and body</li>
* <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper
* wrap} the request to filter HTTP attributes.</li>
* <li>Optionally modify the body of the request.</li>
* <li><strong>Either</strong>
* <ul>
* <li>execute the request using
* {@link ClientHttpRequestExecution#execute(org.springframework.http.HttpRequest, byte[])},</li>
* <strong>or</strong>
* <li>do not execute the request to block the execution altogether.</li>
* </ul>
* <li>Optionally wrap the response to filter HTTP attributes.</li>
* </ol>
* @param request the request, containing method, URI, and headers
* @param body the body of the request
* @param execution the request execution
* @return the response
* @throws IOException in case of I/O errors
*/
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException;
}