本文主要研究一下RestTemplate对HttpClient的适配

ClientHttpRequestFactory

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.
     * <p>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方法

HttpComponentsClientHttpRequestFactory

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

ClientHttpRequest

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

HttpComponentsClientHttpRequest

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

HttpComponentsStreamingClientHttpRequest

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

ClientHttpResponse

org/springframework/http/client/ClientHttpResponse.java

/**
 * Represents a client-side HTTP response.
 * Obtained via an calling of the {@link ClientHttpRequest#execute()}.
 *
 * <p>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方法

HttpComponentsClientHttpResponse

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。


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论