Spring6中RestTemplate配置Apache异步HttpClient及代码疑问
问题1:代码运行机制是什么?BlockingRestTemplateCustomizer先创建、BreweryClient后创建时会抛出异常吗?
运行机制
RestTemplateCustomizer是Spring提供的用于定制RestTemplate的回调接口。当你通过RestTemplateBuilder构建RestTemplate实例时,Builder会自动扫描Spring容器中所有实现了RestTemplateCustomizer的Bean,在RestTemplate实例创建完成后,依次调用每个定制器的customize()方法,完成对RestTemplate的额外配置(比如替换请求工厂、添加消息转换器等)。
关于Bean创建顺序的异常问题
不会抛出异常。原因是BlockingRestTemplateCustomizer的customize()方法并不是在自身Bean初始化阶段执行的——它只是一个等待被RestTemplateBuilder调用的回调逻辑。当BreweryClient依赖RestTemplate时,Spring的依赖注入机制会确保:先通过RestTemplateBuilder创建并完成所有定制(包括调用BlockingRestTemplateCustomizer的customize())的RestTemplate实例,才会被注入给BreweryClient。即便BlockingRestTemplateCustomizer先被创建,它的逻辑触发时机也只和RestTemplate的实例化绑定,和BreweryClient的创建顺序没有直接冲突。
问题2:RestTemplateCustomizer、RestTemplate与RestTemplateBuilder三者的关联是什么?
三者是Spring中构建和定制RestTemplate的核心组件,关系如下:
- RestTemplate:Spring同步HTTP请求的核心客户端类,提供了调用远程API的各种方法,是最终用于发起请求的实例。
- RestTemplateBuilder:RestTemplate的工厂类,提供链式API来设置RestTemplate的基础配置(比如超时时间、默认消息转换器),同时会自动发现并应用容器中所有的
RestTemplateCustomizerBean,实现模块化的定制。 - RestTemplateCustomizer:回调接口,定义了
void customize(RestTemplate restTemplate)方法,用于对已初始化的RestTemplate实例进行细粒度修改。你可以创建多个该接口的实现Bean,RestTemplateBuilder在构建RestTemplate时会自动调用所有定制器的方法,实现配置复用和解耦。
简单总结:RestTemplateBuilder负责创建RestTemplate实例,创建过程中调用所有RestTemplateCustomizer的方法完成定制,最终将配置好的RestTemplate注入给依赖它的Bean(比如BreweryClient)。
问题3:Spring6中无HttpComponentsAsyncClientHttpRequestFactory,如何为RestTemplate配置带连接池的Apache异步HTTP客户端?
Spring6移除了原有的HttpComponentsAsyncClientHttpRequestFactory,可以通过以下步骤手动实现适配:
1. 引入依赖
确保项目中引入Apache HttpClient5的异步相关依赖:
<dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> <dependency> <groupId>org.apache.httpcomponents.core5</groupId> <artifactId>httpcore5</artifactId> <version>5.2.1</version> </dependency>
2. 构建带连接池的异步HTTP客户端
创建配置好连接池的CloseableHttpAsyncClient Bean:
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AsyncHttpClientConfig { @Bean public CloseableHttpAsyncClient asyncHttpClient() { // 配置连接池参数 PoolingAsyncClientConnectionManager connectionManager = new PoolingAsyncClientConnectionManager(); connectionManager.setMaxTotal(100); // 全局最大连接数 connectionManager.setDefaultMaxPerRoute(20); // 单域名最大连接数 // 配置Socket超时等参数 SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(Timeout.ofSeconds(10)) .build(); // 构建异步客户端 return HttpAsyncClients.custom() .setConnectionManager(connectionManager) .setDefaultSocketConfig(socketConfig) .setConnectionTimeToLive(TimeValue.ofMinutes(5)) // 连接存活时间 .build(); } }
3. 实现自定义AsyncClientHttpRequestFactory
Spring6没有提供现成的适配类,需要自己实现AsyncClientHttpRequestFactory来桥接Apache异步客户端和Spring的RestTemplate:
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.client.AbstractAsyncClientHttpRequest; import org.springframework.http.client.AsyncClientHttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.concurrent.CompletableFuture; public class ApacheAsyncClientHttpRequestFactory implements AsyncClientHttpRequestFactory { private final CloseableHttpAsyncClient httpAsyncClient; public ApacheAsyncClientHttpRequestFactory(CloseableHttpAsyncClient httpAsyncClient) { this.httpAsyncClient = httpAsyncClient; } @Override public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException { return new ApacheAsyncClientHttpRequest(httpAsyncClient, uri, httpMethod); } private static class ApacheAsyncClientHttpRequest extends AbstractAsyncClientHttpRequest { private final CloseableHttpAsyncClient httpAsyncClient; private final URI uri; private final HttpMethod httpMethod; public ApacheAsyncClientHttpRequest(CloseableHttpAsyncClient httpAsyncClient, URI uri, HttpMethod httpMethod) { this.httpAsyncClient = httpAsyncClient; this.uri = uri; this.httpMethod = httpMethod; } @Override protected CompletableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, byte[] body) throws IOException { SimpleHttpRequest request = new SimpleHttpRequest(httpMethod.name(), uri); // 复制请求头到Apache请求中 headers.forEach((key, values) -> values.forEach(value -> request.addHeader(key, value))); // 设置请求体 if (body.length > 0) { request.setBody(body); } CompletableFuture<ClientHttpResponse> future = new CompletableFuture<>(); // 执行异步请求 httpAsyncClient.execute(request, new org.apache.hc.client5.http.async.ResponseConsumer<>() { @Override public void consumeResponse(SimpleHttpResponse response) { future.complete(new ApacheAsyncClientHttpResponse(response)); } @Override public void failed(Exception ex) { future.completeExceptionally(ex); } @Override public void releaseResources() { // 释放资源 } }); return future; } @Override public HttpMethod getMethod() { return httpMethod; } @Override public URI getURI() { return uri; } } // 适配Apache响应为Spring的ClientHttpResponse private static class ApacheAsyncClientHttpResponse implements ClientHttpResponse { private final SimpleHttpResponse response; public ApacheAsyncClientHttpResponse(SimpleHttpResponse response) { this.response = response; } @Override public org.springframework.http.HttpStatus getStatusCode() { return org.springframework.http.HttpStatus.valueOf(response.getCode()); } @Override public int getRawStatusCode() { return response.getCode(); } @Override public String getStatusText() { return response.getReasonPhrase(); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); response.getHeaders().forEach(header -> headers.add(header.getName(), header.getValue())); return headers; } @Override public InputStream getBody() throws IOException { return response.getBody(); } @Override public void close() { try { response.getBody().close(); } catch (IOException e) { // 记录日志或忽略 } } } }
4. 配置RestTemplate使用自定义工厂
通过RestTemplateCustomizer将自定义工厂绑定到RestTemplate:
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplateCustomizer; @Configuration public class RestTemplateConfig { @Bean public RestTemplateCustomizer asyncRestTemplateCustomizer(CloseableHttpAsyncClient asyncHttpClient) { return restTemplate -> { restTemplate.setAsyncRequestFactory(new ApacheAsyncClientHttpRequestFactory(asyncHttpClient)); }; } // 如果需要直接构建RestTemplate,也可以这样: @Bean public RestTemplate restTemplate(RestTemplateBuilder builder, RestTemplateCustomizer asyncRestTemplateCustomizer) { return builder.addCustomizers(asyncRestTemplateCustomizer).build(); } }
这样配置后,RestTemplate就可以使用带连接池的Apache异步HTTP客户端发起异步请求了。
内容的提问来源于stack exchange,提问作者ashok

