本文主要研究一下RestTemplate对HttpClient的适配
org/springframework/http/client/ClientHttpRequestFactory.java
/**
* Factory for {@link ClientHttpRequest} objects.
* Requests are created by the {@link #createRequest(URI, HttpMethod)} method.
*
* @author Arjen Poutsma
* @since 3.0
*/
@FunctionalInterface
public interface ClientHttpRequestFactory {
/**
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* The returned request can be written to, and then executed by calling
* {@link ClientHttpRequest#execute()}.
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
spring-web定义了ClientHttpRequestFactory接口,它定义了一个createRequest方法
org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java
public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
private HttpClient httpClient;
@Nullable
private RequestConfig requestConfig;
private boolean bufferRequestBody = true;
/**
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with a default {@link HttpClient} based on system properties.
*/
public HttpComponentsClientHttpRequestFactory() {
this.httpClient = HttpClients.createSystem();
}
/**
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with the given {@link HttpClient} instance.
* @param httpClient the HttpClient instance to use for this request factory
*/
public HttpComponentsClientHttpRequestFactory(HttpClient httpClient) {
this.httpClient = httpClient;
}
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpClient client = getHttpClient();
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
postProcessHttpRequest(httpRequest);
HttpContext context = createHttpContext(httpMethod, uri);
if (context == null) {
context = HttpClientContext.create();
}
// Request configuration not set in the context
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
// Use request configuration given by the user, when available
RequestConfig config = null;
if (httpRequest instanceof Configurable) {
config = ((Configurable) httpRequest).getConfig();
}
if (config == null) {
config = createRequestConfig(client);
}
if (config != null) {
context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
}
}
if (this.bufferRequestBody) {
return new HttpComponentsClientHttpRequest(client, httpRequest, context);
}
else {
return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context);
}
}
【/**
* Shutdown hook that closes the underlying
* {@link org.apache.http.conn.HttpClientConnectionManager ClientConnectionManager}'s
* connection pool, if any.
*/
@Override
public void destroy() throws Exception {
HttpClient httpClient = getHttpClient();
if (httpClient instanceof Closeable) {
((Closeable) httpClient).close();
}
}
//......
}
HttpComponentsClientHttpRequestFactory实现了ClientHttpRequestFactory及DisposableBean接口;createRequest方法先拿到httpClient,然后创建createHttpUriRequest,设置RequestConfig,针对bufferRequestBody的场景HttpComponentsClientHttpRequest,否则创建HttpComponentsStreamingClientHttpRequest;destroy方法主要是关闭httpClient
org/springframework/http/client/ClientHttpRequest.java
public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {
/**
* Execute this request, resulting in a {@link ClientHttpResponse} that can be read.
* @return the response result of the execution
* @throws IOException in case of I/O errors
*/
ClientHttpResponse execute() throws IOException;
}
ClientHttpRequest接口定义了execute方法,它返回ClientHttpResponse
org/springframework/http/client/HttpComponentsClientHttpRequest.java
final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpRequest {
private final HttpClient httpClient;
private final HttpUriRequest httpRequest;
private final HttpContext httpContext;
HttpComponentsClientHttpRequest(HttpClient client, HttpUriRequest request, HttpContext context) {
this.httpClient = client;
this.httpRequest = request;
this.httpContext = context;
}
@Override
public String getMethodValue() {
return this.httpRequest.getMethod();
}
@Override
public URI getURI() {
return this.httpRequest.getURI();
}
HttpContext getHttpContext() {
return this.httpContext;
}
@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
addHeaders(this.httpRequest, headers);
if (this.httpRequest instanceof HttpEntityEnclosingRequest) {
HttpEntityEnclosingRequest entityEnclosingRequest = (HttpEntityEnclosingRequest) this.httpRequest;
HttpEntity requestEntity = new ByteArrayEntity(bufferedOutput);
entityEnclosingRequest.setEntity(requestEntity);
}
HttpResponse httpResponse = this.httpClient.execute(this.httpRequest, this.httpContext);
return new HttpComponentsClientHttpResponse(httpResponse);
}
/**
* Add the given headers to the given HTTP request.
* @param httpRequest the request to add the headers to
* @param headers the headers to add
*/
static void addHeaders(HttpUriRequest httpRequest, HttpHeaders headers) {
headers.forEach((headerName, headerValues) -> {
if (HttpHeaders.COOKIE.equalsIgnoreCase(headerName)) { // RFC 6265
String headerValue = StringUtils.collectionToDelimitedString(headerValues, "; ");
httpRequest.addHeader(headerName, headerValue);
}
else if (!HTTP.CONTENT_LEN.equalsIgnoreCase(headerName) &&
!HTTP.TRANSFER_ENCODING.equalsIgnoreCase(headerName)) {
for (String headerValue : headerValues) {
httpRequest.addHeader(headerName, headerValue);
}
}
});
}
}
HttpComponentsClientHttpRequest继承了AbstractBufferingClientHttpRequest,其executeInternal方法调用httpClient.execute,然后返回HttpComponentsClientHttpResponse
org/springframework/http/client/HttpComponentsStreamingClientHttpRequest.java
final class HttpComponentsStreamingClientHttpRequest extends AbstractClientHttpRequest
implements StreamingHttpOutputMessage {
private final HttpClient httpClient;
private final HttpUriRequest httpRequest;
private final HttpContext httpContext;
@Nullable
private Body body;
HttpComponentsStreamingClientHttpRequest(HttpClient client, HttpUriRequest request, HttpContext context) {
this.httpClient = client;
this.httpRequest = request;
this.httpContext = context;
}
@Override
public String getMethodValue() {
return this.httpRequest.getMethod();
}
@Override
public URI getURI() {
return this.httpRequest.getURI();
}
@Override
public void setBody(Body body) {
assertNotExecuted();
this.body = body;
}
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
throw new UnsupportedOperationException("getBody not supported");
}
@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
HttpComponentsClientHttpRequest.addHeaders(this.httpRequest, headers);
if (this.httpRequest instanceof HttpEntityEnclosingRequest && this.body != null) {
HttpEntityEnclosingRequest entityEnclosingRequest = (HttpEntityEnclosingRequest) this.httpRequest;
HttpEntity requestEntity = new StreamingHttpEntity(getHeaders(), this.body);
entityEnclosingRequest.setEntity(requestEntity);
}
HttpResponse httpResponse = this.httpClient.execute(this.httpRequest, this.httpContext);
return new HttpComponentsClientHttpResponse(httpResponse);
}
//......
}
HttpComponentsStreamingClientHttpRequest继承了AbstractClientHttpRequest,实现了StreamingHttpOutputMessage接口,其getBodyInternal抛出UnsupportedOperationException,其executeInternal方法创建的是StreamingHttpEntity,然后执行httpClient.execute(this.httpRequest, this.httpContext),最后返回HttpComponentsClientHttpResponse
org/springframework/http/client/ClientHttpResponse.java
/**
* Represents a client-side HTTP response.
* Obtained via an calling of the {@link ClientHttpRequest#execute()}.
*
* A {@code ClientHttpResponse} must be {@linkplain #close() closed},
* typically in a {@code finally} block.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface ClientHttpResponse extends HttpInputMessage, Closeable {
/**
* Return the HTTP status code as an {@link HttpStatus} enum value.
* @return the HTTP status as an HttpStatus enum value (never {@code null})
* @throws IOException in case of I/O errors
* @throws IllegalArgumentException in case of an unknown HTTP status code
* @since #getRawStatusCode()
* @see HttpStatus#valueOf(int)
*/
HttpStatus getStatusCode() throws IOException;
/**
* Return the HTTP status code (potentially non-standard and not
* resolvable through the {@link HttpStatus} enum) as an integer.
* @return the HTTP status as an integer value
* @throws IOException in case of I/O errors
* @since 3.1.1
* @see #getStatusCode()
* @see HttpStatus#resolve(int)
*/
int getRawStatusCode() throws IOException;
/**
* Return the HTTP status text of the response.
* @return the HTTP status text
* @throws IOException in case of I/O errors
*/
String getStatusText() throws IOException;
/**
* Close this response, freeing any resources created.
*/
@Override
void close();
}
ClientHttpResponse接口定义了getStatusCode、getRawStatusCode、getStatusText、close方法
org/springframework/http/client/HttpComponentsClientHttpResponse.java
final class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
private final HttpResponse httpResponse;
@Nullable
private HttpHeaders headers;
HttpComponentsClientHttpResponse(HttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
@Override
public int getRawStatusCode() throws IOException {
return this.httpResponse.getStatusLine().getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return this.httpResponse.getStatusLine().getReasonPhrase();
}
@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new HttpHeaders();
for (Header header : this.httpResponse.getAllHeaders()) {
this.headers.add(header.getName(), header.getValue());
}
}
return this.headers;
}
@Override
public InputStream getBody() throws IOException {
HttpEntity entity = this.httpResponse.getEntity();
return (entity != null ? entity.getContent() : StreamUtils.emptyInput());
}
@Override
public void close() {
// Release underlying connection back to the connection manager
try {
try {
// Attempt to keep connection alive by consuming its remaining content
EntityUtils.consume(this.httpResponse.getEntity());
}
finally {
if (this.httpResponse instanceof Closeable) {
((Closeable) this.httpResponse).close();
}
}
}
catch (IOException ex) {
// Ignore exception on close...
}
}
}
HttpComponentsClientHttpResponse继承了AbstractClientHttpResponse,其getBody方法返回的是httpResponse.getEntity().getContent()或者StreamUtils.emptyInput(),其close方法主要是执行EntityUtils.consume(this.httpResponse.getEntity())以及((Closeable) this.httpResponse).close()
spring-web定义了ClientHttpRequestFactory接口,它定义了一个createRequest方法,HttpComponentsClientHttpRequestFactory就是RestTemplate对HttpClient的适配,其通过HttpComponentsClientHttpRequest或者HttpComponentsStreamingClientHttpRequest,将HttpClient的request适配为了spring-web的ClientHttpRequest,将response通过HttpComponentsClientHttpResponse适配为spring-web的ClientHttpResponse。