Spring Boot 3.x.x集成HttpClient4遇编译错误求助
问题描述
使用Spring Boot 3.4.1时,框架默认集成Apache HttpClient5,但需要自定义RestTemplate使用HttpClient4,配置后出现编译错误:HttpComponentsClientHttpRequestFactory的构造器参数要求为HttpClient5类型,而非HttpClient4的org.apache.http.client.HttpClient。
已执行的操作:
- 在
pom.xml中排除HttpClient5依赖,引入HttpClient4及对应兼容版本的httpcore - 在主类中排除
RestTemplateAutoConfiguration和HttpClientAutoConfiguration自动配置类
核心原因
Spring Boot 3基于Spring 6,而Spring 6中的org.springframework.http.client.HttpComponentsClientHttpRequestFactory仅适配HttpClient5的API,不再支持HttpClient4的org.apache.http.client.HttpClient类型,导致参数类型不匹配。
解决方案
1. 彻底排除HttpClient5依赖
确保项目中无任何HttpClient5的依赖,可通过mvn dependency:tree命令检查依赖树,确认没有org.apache.httpcomponents:httpclient5的引用。
修改pom.xml,除了排除spring-boot-starter-web中的httpclient5,若引入了spring-boot-starter-httpclient也需一并排除:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient5</artifactId> </exclusion> </exclusions> </dependency> <!-- 若引入了starter-httpclient,添加以下排除配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-httpclient</artifactId> <exclusions> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient5</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.14</version> </dependency>
2. 自定义适配HttpClient4的ClientHttpRequestFactory
由于Spring 6原生的HttpComponentsClientHttpRequestFactory不再支持HttpClient4,需自定义一个适配类:
import org.apache.http.client.HttpClient; import org.apache.http.client.methods.*; import org.springframework.http.HttpMethod; import org.springframework.http.client.*; import java.io.IOException; import java.io.InputStream; import java.net.URI; public class HttpClient4RequestFactory extends AbstractClientHttpRequestFactoryWrapper { private final HttpClient httpClient; public HttpClient4RequestFactory(HttpClient httpClient) { super(null); this.httpClient = httpClient; } @Override protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException { HttpRequestBase httpRequest = switch (httpMethod) { case GET -> new HttpGet(uri); case POST -> new HttpPost(uri); case PUT -> new HttpPut(uri); case DELETE -> new HttpDelete(uri); case HEAD -> new HttpHead(uri); case OPTIONS -> new HttpOptions(uri); case PATCH -> new HttpPatch(uri); default -> throw new IllegalArgumentException("Unsupported HTTP method: " + httpMethod); }; return new HttpClient4Request(httpClient, httpRequest); } private static class HttpClient4Request extends AbstractClientHttpRequest { private final HttpClient httpClient; private final HttpRequestBase httpRequest; public HttpClient4Request(HttpClient httpClient, HttpRequestBase httpRequest) { this.httpClient = httpClient; this.httpRequest = httpRequest; } @Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { headers.forEach((key, values) -> httpRequest.setHeader(key, String.join(", ", values))); return new HttpClient4Response(httpClient.execute(httpRequest)); } @Override public HttpMethod getMethod() { return HttpMethod.valueOf(httpRequest.getMethod()); } @Override public URI getURI() { return httpRequest.getURI(); } } private static class HttpClient4Response extends AbstractClientHttpResponse { private final org.apache.http.HttpResponse httpResponse; public HttpClient4Response(org.apache.http.HttpResponse httpResponse) { this.httpResponse = httpResponse; } @Override public int getRawStatusCode() throws IOException { return httpResponse.getStatusLine().getStatusCode(); } @Override public String getStatusText() throws IOException { return httpResponse.getStatusLine().getReasonPhrase(); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); httpResponse.getAllHeaders().forEach(header -> headers.add(header.getName(), header.getValue())); return headers; } @Override public InputStream getBody() throws IOException { return httpResponse.getEntity().getContent(); } @Override public void close() { try { httpResponse.getEntity().getContent().close(); } catch (IOException e) { // 忽略关闭异常 } } } }
3. 修改RestTemplate配置类
将原配置中的HttpComponentsClientHttpRequestFactory替换为自定义的HttpClient4RequestFactory:
@Configuration public class BlockingRestTemplateCustomizer { @Bean("restTemplate") public RestTemplate restTemplate() { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(100); connectionManager.setDefaultMaxPerRoute(20); RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(3000) .build(); HttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .build(); // 使用自定义的RequestFactory适配HttpClient4 ClientHttpRequestFactory factory = new HttpClient4RequestFactory(httpClient); return new RestTemplate(factory); } }
4. 确认自动配置排除
确保主类中已排除相关自动配置,避免框架默认配置冲突:
@SpringBootApplication @EnableAutoConfiguration(exclude = { RestTemplateAutoConfiguration.class, HttpClientAutoConfiguration.class }) public class MsscBreweryClientApplication { public static void main(String[] args) { SpringApplication.run(MsscBreweryClientApplication.class, args); } }
说明
Spring官方在Spring 6及Spring Boot 3中已停止对HttpClient4的支持,推荐迁移至HttpClient5以获得更好的性能和长期维护性。若因特殊需求必须使用HttpClient4,可通过上述自定义适配类实现集成。
内容的提问来源于stack exchange,提问作者Neeraj




