RestTemplate对HttpClient的适配源码解读

 更新时间:2023年10月09日 08:36:48   作者:codecraft  
这篇文章主要为大家介绍了RestTemplate对HttpClient的适配源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

本文主要研究一下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。

以上就是RestTemplate对HttpClient的适配的详细内容,更多关于RestTemplate HttpClient适配的资料请关注脚本之家其它相关文章!

相关文章

  • Java中的可重入锁ReentrantLock简析

    Java中的可重入锁ReentrantLock简析

    这篇文章主要介绍了Java中的可重入锁ReentrantLock简析,可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住,需要的朋友可以参考下
    2023-12-12
  • 一篇文章带你深入了解Java类加载

    一篇文章带你深入了解Java类加载

    这篇文章主要介绍了Java中类加载过程全面解析,具有一定参考价值,需要的朋友可以了解下,希望能给你带来帮助
    2021-08-08
  • Windows安装Maven并配置环境的详细步骤

    Windows安装Maven并配置环境的详细步骤

    Maven是一个非常流行的构建和项目管理工具,用于Java开发,它提供了一个强大的依赖管理系统和一系列标准化的构建生命周期,本文将指导您如何在Windows操作系统上安装和配置Maven,需要的朋友可以参考下
    2023-05-05
  • 深入剖析Java中Map.Entry的方法与实战应用

    深入剖析Java中Map.Entry的方法与实战应用

    在Java集合框架中,Map.Entry扮演着连接键值对的桥梁角色,作为Map接口的内部接口,它封装了键值对的本质,是高效处理映射数据的核心工具,下面我我们就来深入剖析Map.Entry的概念,方法及实战应用
    2025-06-06
  • Java中对集合的元素排序操作方法

    Java中对集合的元素排序操作方法

    本文介绍了Java中对集合元素进行排序的几种常见方法,包括使用Collections.sort()、List.sort()、StreamAPI以及对Map的键或值进行排序,每种方法都有其适用的场景和使用方式,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • Java的MyBatis快速入门和实战详解

    Java的MyBatis快速入门和实战详解

    这篇文章主要介绍了Java的MyBatis快速入门和实战详解,MyBatis是一款优秀的持久层框架,用于简化JDBC开发,是一套可重用的,通用的,软件基础代码模型,需要的朋友可以参考下
    2023-05-05
  • Spring Boot项目中实现文件上传功能的示例

    Spring Boot项目中实现文件上传功能的示例

    这篇文章主要介绍了Spring Boot项目中实现文件上传功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页示例

    Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页示例

    本文介绍了Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Java并发编程中的Callable、Future和FutureTask详解

    Java并发编程中的Callable、Future和FutureTask详解

    这篇文章主要介绍了Java并发编程中的Callable、Future和FutureTask详解,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口,这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果,需要的朋友可以参考下
    2023-07-07
  • Spring框架 注解配置事务控制的流程

    Spring框架 注解配置事务控制的流程

    这篇文章主要介绍了Spring框架 注解配置事务控制的流程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论