You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring6中RestTemplate配置Apache异步HttpClient及代码疑问

RestTemplate配置相关问题解答

问题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的基础配置(比如超时时间、默认消息转换器),同时会自动发现并应用容器中所有的RestTemplateCustomizer Bean,实现模块化的定制。
  • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:23:11