RestTemplate對(duì)HttpClient的適配源碼解讀
序
本文主要研究一下RestTemplate對(duì)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接口,它定義了一個(gè)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實(shí)現(xiàn)了ClientHttpRequestFactory及DisposableBean接口;createRequest方法先拿到httpClient,然后創(chuàng)建createHttpUriRequest,設(shè)置RequestConfig,針對(duì)bufferRequestBody的場(chǎng)景HttpComponentsClientHttpRequest,否則創(chuàng)建HttpComponentsStreamingClientHttpRequest;destroy方法主要是關(guān)閉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方法調(diào)用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,實(shí)現(xiàn)了StreamingHttpOutputMessage接口,其getBodyInternal拋出UnsupportedOperationException,其executeInternal方法創(chuàng)建的是StreamingHttpEntity,然后執(zhí)行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方法主要是執(zhí)行EntityUtils.consume(this.httpResponse.getEntity())以及((Closeable) this.httpResponse).close()
小結(jié)
spring-web定義了ClientHttpRequestFactory接口,它定義了一個(gè)createRequest方法,HttpComponentsClientHttpRequestFactory就是RestTemplate對(duì)HttpClient的適配,其通過(guò)HttpComponentsClientHttpRequest或者HttpComponentsStreamingClientHttpRequest,將HttpClient的request適配為了spring-web的ClientHttpRequest,將response通過(guò)HttpComponentsClientHttpResponse適配為spring-web的ClientHttpResponse。
以上就是RestTemplate對(duì)HttpClient的適配的詳細(xì)內(nèi)容,更多關(guān)于RestTemplate HttpClient適配的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 使用Backoff策略提高HttpClient連接管理的效率
- 提升網(wǎng)絡(luò)請(qǐng)求穩(wěn)定性HttpClient的重試機(jī)制深入理解
- NoHttpResponseException異常解決優(yōu)化HttpClient配置以避免連接問(wèn)題
- 簡(jiǎn)化API提升開發(fā)效率RestTemplate與HttpClient?OkHttp關(guān)系詳解
- HttpClient的KeepAlive接口方法源碼解析
- 深入了解HttpClient的ResponseHandler接口
- HttpClient HttpRoutePlanner接口確定請(qǐng)求目標(biāo)路由
相關(guān)文章
MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱
MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架,MyBatis越來(lái)越受大家的喜愛(ài)了。下面給大家分享MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱,感興趣的朋友一起看看吧2016-10-10利用Springboot+Caffeine實(shí)現(xiàn)本地緩存實(shí)例代碼
Caffeine是一個(gè)基于Java8開發(fā)的提供了近乎最佳命中率的高性能的緩存庫(kù),下面這篇文章主要給大家介紹了關(guān)于利用Springboot+Caffeine實(shí)現(xiàn)本地緩存的相關(guān)資料,需要的朋友可以參考下2023-01-01Java創(chuàng)建數(shù)組、賦值的四種方式詳解(聲明+創(chuàng)建+初始化?)
數(shù)組是一種數(shù)據(jù)結(jié)構(gòu),用來(lái)存儲(chǔ)同一類型值的集合一旦創(chuàng)建了數(shù)組,就不能再改變它的長(zhǎng)度,下面這篇文章主要給大家介紹了關(guān)于Java創(chuàng)建數(shù)組、賦值的四種方式(聲明+創(chuàng)建+初始化?)的相關(guān)資料,需要的朋友可以參考下2024-04-04Java Web 簡(jiǎn)單的分頁(yè)顯示實(shí)例代碼
這篇文章主要介紹了Java Web 簡(jiǎn)單的分頁(yè)顯示實(shí)例代碼的相關(guān)資料,本文通過(guò),計(jì)算總的頁(yè)數(shù)和查詢指定頁(yè)數(shù)據(jù)兩個(gè)方法實(shí)現(xiàn)分頁(yè)效果,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06SpringBoot接口防抖(防重復(fù)提交)的實(shí)現(xiàn)方案
所謂防抖,一是防用戶手抖,二是防網(wǎng)絡(luò)抖動(dòng),在Web系統(tǒng)中,表單提交是一個(gè)非常常見(jiàn)的功能,如果不加控制,容易因?yàn)橛脩舻恼`操作或網(wǎng)絡(luò)延遲導(dǎo)致同一請(qǐng)求被發(fā)送多次,所以本文給大家介紹了SpringBoot接口防抖(防重復(fù)提交)的實(shí)現(xiàn)方案,需要的朋友可以參考下2024-04-04IDEA編譯時(shí)報(bào)常量字符串過(guò)長(zhǎng)的解決辦法
本文主要介紹了IDEA編譯時(shí)報(bào)常量字符串過(guò)長(zhǎng)的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07解決SSLContext.getInstance()中參數(shù)設(shè)置TLS版本無(wú)效的問(wèn)題
這篇文章主要介紹了解決SSLContext.getInstance()中參數(shù)設(shè)置TLS版本無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java實(shí)現(xiàn)給網(wǎng)站上傳圖片蓋章的方法
這篇文章主要介紹了Java實(shí)現(xiàn)給網(wǎng)站上傳圖片蓋章的方法,涉及java針對(duì)圖片的合成操作技巧,類似水印功能,需要的朋友可以參考下2015-07-07