Apache HttpClient 5.x 完整教程
HttpClient 是一个流行的、功能强大的 Java HTTP 客户端库,用于发送 HTTP 请求和处理 HTTP 响应,它被广泛应用于需要与 Web 服务、REST API 进行交互的 Java 应用程序中。

重要提示: 本教程基于 HttpClient 5.x 版本,这是当前的主流版本,它与 4.x 版本在 API 设计上有显著不同,尤其是在异步请求和资源管理方面。
目录
- 为什么选择 HttpClient?
- 环境准备
- 核心概念
- 发送第一个 GET 请求
- 发送 POST 请求 (提交表单/JSON)
- 处理响应
- 高级主题
- 1 设置请求头
- 2 处理重定向
- 3 处理 Cookie
- 4 连接池
- 5 超时设置
- 异步请求
- 最佳实践与资源管理
为什么选择 HttpClient?
相比于 Java 内置的 HttpURLConnection,HttpClient 提供了以下优势:
- 更丰富的 API:API 设计更现代化,易于使用。
- 更强大的功能:内置了对连接池、重定向、Cookie、压缩等高级特性的支持。
- 更灵活的请求构建:可以轻松地构建复杂的 HTTP 请求。
- 更好的性能:通过连接池可以显著提高高并发场景下的性能。
- 同步和异步支持:既支持传统的同步阻塞式调用,也支持基于回调的非阻塞式异步调用。
环境准备
你需要在你的项目中添加 HttpClient 5 的依赖。
Maven (pom.xml)
<dependencies>
<!-- HttpClient 核心库 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 用于处理 JSON 的常用库 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 如果需要异步支持,需要添加这个 -->
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5-reactive</artifactId>
<version>5.2</version> <!-- 请使用最新版本 -->
</dependency>
</dependencies>
Gradle (build.gradle)
dependencies {
// HttpClient 核心库
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'
// 用于处理 JSON 的常用库
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
// 如果需要异步支持,需要添加这个
implementation 'org.apache.httpcomponents.core5:httpcore5-reactive:5.2'
}
核心概念
在学习代码之前,了解几个核心类非常重要:

HttpClient: 执行 HTTP 请求的入口点,它管理着连接池、执行上下文等,通常建议为整个应用创建一个单例的HttpClient实例。HttpRequest: 代表一个 HTTP 请求的接口,最常用的实现是HttpRequestBase的子类,如HttpGet,HttpPost,HttpPut,HttpDelete等。HttpResponse: 代表一个 HTTP 响应,它包含状态码、响应头和响应实体。HttpEntity: 代表 HTTP 消息的正文(请求体或响应体),可以看作是数据的容器。CloseableHttpResponse:HttpResponse的一个可关闭版本,使用后必须关闭以释放资源。ResponseHandler: 一个非常有用的接口,可以让你将HttpResponse直接转换为特定类型的 Java 对象,从而简化代码。
发送第一个 GET 请求
这是最简单的用例:从指定的 URL 获取数据。
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
public class SimpleGetRequest {
public static void main(String[] args) {
// 1. 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 2. 创建 HttpGet 请求,可以添加查询参数
URIBuilder uriBuilder = new URIBuilder("https://jsonplaceholder.typicode.com/posts/1");
HttpGet httpGet = new HttpGet(uriBuilder.build());
System.out.println("Executing request: " + httpGet.getMethod() + " " + httpGet.getUri());
// 3. 执行请求,并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 4. 获取响应状态码
System.out.println("Response status: " + response.getCode());
// 5. 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response body: " + responseBody);
// 6. 确保响应实体被完全消费并释放连接
EntityUtils.consume(response.getEntity());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
try-with-resources语句用于创建CloseableHttpClient和CloseableHttpResponse,确保它们在使用后能被自动关闭,避免资源泄漏。HttpGet是 GET 请求的具体实现类。URIBuilder是一个方便的工具类,用于构建带有查询参数的 URL,可以避免手动拼接字符串带来的问题。httpClient.execute(httpGet)会阻塞当前线程,直到服务器返回响应。response.getCode()获取 HTTP 状态码,如200,404,500等。EntityUtils.toString(response.getEntity())将响应体(通常是 JSON 或 HTML 字符串)读取为String。这是一个非常重要的步骤,如果读取后不消费掉实体,连接可能无法被正确释放。
发送 POST 请求
POST 请求通常用于向服务器提交数据,例如表单数据或 JSON 对象。
1 提交 JSON 数据
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
public class PostJsonRequest {
public static void main(String[] args) {
String apiUrl = "https://jsonplaceholder.typicode.com/posts";
// 创建一个 ObjectMapper 用于处理 JSON
ObjectMapper objectMapper = new ObjectMapper();
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 1. 创建 HttpPost 请求
HttpPost httpPost = new HttpPost(apiUrl);
// 2. 创建要发送的 JSON 对象
// 这里用一个简单的 Map 代替 Java 对象,更直观
// 实际项目中,你会创建一个对应的 Java 类(如 Post.java)
String jsonPayload = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";
// 3. 设置请求体
StringEntity entity = new StringEntity(jsonPayload, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 4. 设置请求头
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
System.out.println("Executing request: " + httpPost.getMethod() + " " + httpPost.getUri());
System.out.println("Request payload: " + jsonPayload);
// 5. 执行请求
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
System.out.println("Response status: " + response.getCode());
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response body: " + responseBody);
EntityUtils.consume(response.getEntity());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2 提交表单数据
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.UrlEncodedFormEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PostFormRequest {
public static void main(String[] args) {
String apiUrl = "https://example.com/api/login"; // 这是一个示例 URL
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(apiUrl);
// 1. 创建表单参数列表
List<BasicNameValuePair> formParams = new ArrayList<>();
formParams.add(new BasicNameValuePair("username", "john.doe"));
formParams.add(new BasicNameValuePair("password", "secret123"));
// 2. 设置请求体
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams);
httpPost.setEntity(entity);
System.out.println("Executing request: " + httpPost.getMethod() + " " + httpPost.getUri());
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
System.out.println("Response status: " + response.getCode());
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response body: " + responseBody);
EntityUtils.consume(response.getEntity());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
处理响应
手动解析状态码和响应体有些繁琐。ResponseHandler 可以让这个过程更优雅。
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.classic.BasicResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
public class ResponseHandlerExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
// 创建一个 ResponseHandler,它会自动处理响应和资源释放
BasicResponseHandler<String> responseHandler = new BasicResponseHandler<>();
// execute 方法现在直接返回响应体的字符串
String responseBody = httpClient.execute(httpGet, responseHandler);
System.out.println("Response body (via ResponseHandler):");
System.out.println(responseBody);
} catch (Exception e) {
e.printStackTrace();
}
}
}
优点:
- 代码更简洁,无需手动
try-with-resources来管理CloseableHttpResponse。 ResponseHandler内部会处理EntityUtils.consume(),确保资源被释放。
高级主题
1 设置请求头
在之前的 POST 请求示例中已经展示了如何设置请求头,使用 httpGet.setHeader("Header-Name", "Header-Value") 即可。
HttpGet httpGet = new Get("http://example.com");
httpGet.setHeader("User-Agent", "My-Cool-App/1.0");
httpGet.setHeader("Accept-Language", "en-US,en;q=0.9");
2 处理重定向
HttpClient 默认会自动处理重定向(从 HTTP 跳转到 HTTPS,或者 301/302 跳转),你可以通过 HttpClientBuilder 进行配置。
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpHost;
RequestConfig config = RequestConfig.custom()
.setRedirectsEnabled(false) // 禁用自动重定向
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(config)
.build()) {
// ...
}
3 处理 Cookie
HttpClient 内置了 Cookie 管理功能。
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.cookie.BasicCookieStore;
import org.apache.hc.client5.http.impl.cookie.PublicSuffixMatcher;
import org.apache.hc.client5.http.impl.cookie.PublicSuffixMatcherLoader;
import org.apache.hc.client5.http.impl.cookie.StandardCookieSpec;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.cookie.CookieStore;
// 创建一个自定义的 CookieStore
CookieStore cookieStore = new BasicCookieStore();
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
StandardCookieSpec cookieSpec = StandardCookieSpec.builder().setPublicSuffixMatcher(publicSuffixMatcher).build();
HttpClientContext context = HttpClientContext.create();
context.setCookieStore(cookieStore);
context.setCookieSpecRegistry(cookieSpec);
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build()) {
// 第一次请求,服务器会设置 Cookie
HttpGet get1 = new HttpGet("http://example.com/login");
httpClient.execute(get1, context);
// 第二次请求,HttpClient 会自动带上之前设置的 Cookie
HttpGet get2 = new HttpGet("http://example.com/profile");
try (CloseableHttpResponse response = httpClient.execute(get2, context)) {
// ...
}
}
4 连接池
在高并发场景下,为每个请求创建新连接是非常低效的,连接池可以复用 TCP 连接,大幅提升性能。
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.pool.ConnPoolControl;
import org.apache.hc.core5.reactor.ConnectingIOReactor;
import org.apache.hc.core5.reactor.ConnectingIOReactorConfig;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.reactor.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.util.concurrent.TimeUnit;
public class ConnectionPoolExample {
public static void main(String[] args) throws Exception {
// 1. 创建连接池配置
ConnectingIOReactorConfig ioReactorConfig = ConnectingIOReactorConfig.custom()
.setIoThreadCount(Runtime.getRuntime().availableProcessors()) // 通常设置为 CPU 核心数
.setConnectTimeout(5, TimeUnit.SECONDS)
.setSocketTimeout(5, TimeUnit.SECONDS)
.build();
// 2. 创建 IOReactor
try (ConnectingIOReactor ioReactor = new ConnectingIOReactor(ioReactorConfig)) {
// 3. 创建 HttpClient 配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5, TimeUnit.SECONDS)
.setConnectionRequestTimeout(5, TimeUnit.SECONDS)
.build();
// 4. 使用 IOReactor 创建 HttpClient
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(new PoolingHttpClientConnectionManager(ioReactor))
.setDefaultRequestConfig(requestConfig)
.build()) {
// ... 在这里执行多个 HTTP 请求 ...
// 连接池会自动管理连接的获取和释放
System.out.println("Connection pool created. Ready to make requests.");
}
}
}
}
注意: 在生产环境中,你应该创建一个全局的 HttpClient 单例,而不是每次请求都创建新的。
5 超时设置
超时设置对于构建健壮的客户端至关重要。
- 连接超时: 客户端与服务器建立 TCP 连接的最长时间。
- Socket 超时 (读取超时): 客户端从服务器读取数据的最长时间。
- 请求获取超时: 从连接池中获取一个连接的最长时间。
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(10, TimeUnit.SECONDS) // 连接超时
.setConnectionRequestTimeout(5, TimeUnit.SECONDS) // 从连接池获取连接超时
.setResponseTimeout(30, TimeUnit.SECONDS) // 等待响应超时 (HttpClient 5.x 新增)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(config)
.build()) {
// ...
}
异步请求
HttpClient 5.x 提供了基于 CompletableFuture 的异步 API,非常适合在非阻塞应用(如 Spring WebFlux)或需要高吞吐量的场景中使用。
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.nio.entity.StringEntity;
import java.util.concurrent.CompletableFuture;
public class AsyncRequestExample {
public static void main(String[] args) throws Exception {
// 1. 创建异步 HttpClient
try (CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault()) {
// 2. 启动客户端
httpClient.start();
// 3. 构建异步请求
SimpleHttpRequest request = SimpleRequestBuilder.get("https://jsonplaceholder.typicode.com/posts/1")
.build();
System.out.println("Executing async request...");
// 4. 执行请求并使用回调处理结果
CompletableFuture<SimpleHttpResponse> future = new CompletableFuture<>();
httpClient.execute(request, new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse response) {
System.out.println("Async request completed. Status: " + response.getCode());
future.complete(response);
}
@Override
public void failed(Exception ex) {
System.err.println("Async request failed: " + ex.getMessage());
future.completeExceptionally(ex);
}
@Override
public void cancelled() {
System.err.println("Async request cancelled.");
future.cancel(true);
}
});
// 5. 可以在这里做其他事情,或者等待结果
future.thenAccept(response -> {
try {
System.out.println("Async Response Body: " + response.getBodyText());
} catch (Exception e) {
e.printStackTrace();
}
}).exceptionally(ex -> {
System.err.println("Error in future: " + ex.getMessage());
return null;
});
// 等待异步操作完成(在实际应用中,通常不需要这样做)
future.join();
System.out.println("Async execution finished.");
}
}
}
最佳实践与资源管理
- 重用
HttpClient实例:HttpClient的设计是线程安全的,并且内部管理着昂贵的资源(如连接池),你应该为你的整个应用程序创建一个HttpClient单例,而不是为每个请求都创建一个新的。 - 总是使用
try-with-resources:对于所有实现了Closeable或AutoCloseable的资源(如CloseableHttpClient,CloseableHttpResponse),都应该使用try-with-resources语句来确保它们被正确关闭。 - 消费响应实体:在读取完响应体后,务必调用
EntityUtils.consume(response.getEntity())或使用ResponseHandler,否则连接可能无法被放回连接池,最终导致资源耗尽。 - 配置连接池和超时:在生产环境中,务必根据你的应用需求合理配置连接池大小、超时时间等参数。
- 处理异常:网络请求是不可靠的,要妥善处理可能发生的
IOException、TimeoutException等异常。
本教程涵盖了 Apache HttpClient 5.x 的核心用法,包括:
- 同步请求:
GET,POST的基本用法。 - 请求与响应:如何构建请求、设置请求头、处理响应体。
- 高级功能:连接池、Cookie、重定向、超时配置。
- 异步请求:使用
CompletableFuture进行非阻塞调用。 - 最佳实践:强调资源管理和性能优化。
HttpClient 是一个功能强大的工具,掌握它将使你在处理 HTTP 通信时更加得心应手,建议你结合官方文档和本教程,通过实际项目来练习和巩固这些知识。
