HttpClient 是 Java 生态中最流行的 HTTP 客户端库之一,它提供了非常灵活和强大的代理配置功能,无论是简单的 HTTP 代理、需要身份验证的代理,还是 SOCKS 代理,HttpClient 都能很好地支持。

核心概念:RoutePlanner 和 HttpClientContext
在深入代码之前,理解 HttpClient 中代理的两个核心组件很重要:
RoutePlanner(路由规划器):这是 HttpClient 决定是否使用代理以及使用哪个代理的“大脑”,默认情况下,它会遵循标准的 Java 系统属性(如http.proxyHost,https.proxyHost)以及目标服务器的响应(如果服务器返回3xx重定向或407 Proxy Authentication Required)。HttpClientContext(HTTP客户端上下文):这是一个可变的对象,用于在单个请求执行过程中传递状态信息,包括路由信息、认证凭据、Cookie 等,当你需要为特定请求强制使用代理时,通常会在这个上下文中设置。
基本代理配置(HTTP/HTTPS 代理)
这是最常见的场景,假设你有一个代理服务器地址和端口。
使用 SystemDefaultRoutePlanner(推荐)
这是最简单的方法,它直接读取并使用 Java 的标准系统属性来配置代理,你不需要在代码中创建复杂的对象。
步骤:

- 在启动 Java 应用程序之前,设置系统属性。
- 创建
HttpClient时,使用SystemDefaultRoutePlanner。
示例代码:
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.routing.SystemDefaultRoutePlanner;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.net.Proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class SimpleProxyExample {
public static void main(String[] args) throws IOException, URISyntaxException {
// --- 方式 A: 通过 JVM 启动参数设置 ---
// java -Dhttp.proxyHost=your_proxy_host -Dhttp.proxyPort=8080 -Dhttps.proxyHost=your_proxy_host -Dhttps.proxyPort=8080 YourClassName
// --- 方式 B: 在代码中设置系统属性 (推荐用于测试) ---
// System.setProperty("http.proxyHost", "your_proxy_host");
// System.setProperty("http.proxyPort", "8080");
// System.setProperty("https.proxyHost", "your_proxy_host");
// System.setProperty("https.proxyPort", "8080");
// 创建一个使用系统默认代理的路由规划器
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
// 使用这个路由规划器构建 HttpClient
try (CloseHttpClient httpClient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build()) {
// 创建一个 HTTP GET 请求
// URIBuilder 可以帮助你安全地构建 URL
URI uri = new URIBuilder("http://httpbin.org/get")
.addParameter("user-agent", "My-HttpClient-App")
.build();
HttpGet httpGet = new HttpGet(uri);
System.out.println("Executing request to: " + httpGet.getUri());
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
System.out.println("Response status: " + response.getCode());
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity);
System.out.println("Response body: " + result);
EntityUtils.consume(entity); // 确保实体内容被完全消费,释放连接
}
}
}
}
}
如何运行:
- 启动参数方式:编译后,在命令行运行:
java -Dhttp.proxyHost=proxy.example.com -Dhttp.proxyPort=3128 -Dhttps.proxyHost=proxy.example.com -Dhttps.proxyPort=3128 YourClassName
- 代码设置方式:直接取消注释
System.setProperty(...)部分并填入你的代理信息。
需要身份验证的代理
如果代理服务器需要用户名和密码,你需要使用 ProxyAuthenticationStrategy 和 CredentialsProvider。
核心组件:

CredentialsProvider:用于存储用户名和密码凭据。ProxyAuthenticationStrategy:一个认证策略,当收到407 Proxy Authentication Required响应时,它会自动从CredentialsProvider中查找凭据并重试请求。
示例代码:
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.routing.SystemDefaultRoutePlanner;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.net.Proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class AuthenticatedProxyExample {
public static void main(String[] args) throws IOException, URISyntaxException {
String proxyHost = "your_proxy_host";
int proxyPort = 8080;
String proxyUser = "your_username";
String proxyPass = "your_password";
// 1. 创建凭据提供者
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
// 设置代理作用域和凭据
credentialsProvider.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPass.toCharArray()));
// 2. 创建路由规划器
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
// 3. 创建 HttpClient 并配置凭据提供者和认证策略
try (CloseHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.setRoutePlanner(routePlanner)
.build()) {
URI uri = new URIBuilder("http://httpbin.org/get").build();
org.apache.hc.client5.http.classic.methods.HttpGet httpGet = new org.apache.hc.client5.http.classic.methods.HttpGet(uri);
System.out.println("Executing request through authenticated proxy...");
try (org.apache.hc.client5.http.impl.classic.CloseableHttpResponse response = httpClient.execute(httpGet)) {
System.out.println("Response status: " + response.getCode());
// ... 处理响应
}
}
}
}
为单个请求设置代理
如果你不希望全局设置代理,而是希望只为某一个或几个特定的请求使用代理,可以使用 HttpClientContext。
核心组件:
HttpRoute:表示一个从客户端到目标服务器的完整路由,包括是否经过代理。RequestConfig:用于配置单个请求的执行参数,比如超时、连接请求、以及最重要的——代理。
示例代码:
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.net.Proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class PerRequestProxyExample {
public static void main(String[] args) throws IOException, URISyntaxException {
String proxyHost = "your_proxy_host";
int proxyPort = 8080;
try (CloseHttpClient httpClient = HttpClients.createDefault()) {
URI targetUri = new URIBuilder("http://httpbin.org/ip").build();
HttpGet httpGet = new HttpGet(targetUri);
// 1. 创建一个上下文对象
HttpClientContext context = HttpClientContext.create();
// 2. 创建一个代理地址
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
// 3. 创建请求配置,并设置代理
org.apache.hc.client5.http.config.RequestConfig config = org.apache.hc.client5.http.config.RequestConfig.custom()
.setProxy(proxy)
.build();
// 4. 将配置应用到请求上下文中
context.setRequestConfig(config);
System.out.println("Executing request with a specific proxy...");
// 5. 使用这个上下文执行请求
try (CloseableHttpResponse response = httpClient.execute(httpGet, context)) {
System.out.println("Response status: " + response.getCode());
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity);
System.out.println("Response body: " + result);
EntityUtils.consume(entity);
}
}
}
}
}
这种方式非常灵活,适合在复杂的业务场景中动态选择代理。
SOCKS 代理支持
HttpClient 本身不直接实现 SOCKS 协议,但它可以与 Java 的 java.net.Proxy 集成,从 Java 7 开始,Proxy 类支持 SOCKS。
步骤:
- 创建一个
java.net.Proxy对象,类型为Proxy.Type.SOCKS。 - 创建一个实现
org.apache.hc.client5.http.routing.HttpRoutePlanner接口的自定义路由规划器。 - 在路由规划器的
determineRoute方法中,返回一个通过HttpHost.from(Proxy)创建的路由。
示例代码:
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.impl.classic.CloseHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.net.Proxy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
public class SocksProxyExample {
public static void main(String[] args) throws IOException, URISyntaxException {
String socksHost = "127.0.0.1";
int socksPort = 1080;
// 1. 创建一个 SOCKS 代理选择器
java.net.Proxy socksProxy = new java.net.Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress(socksHost, socksPort));
ProxySelector proxySelector = ProxySelector.of(null); // 清空默认代理
proxySelector = new CustomProxySelector(proxySelector, socksProxy);
// 2. 创建使用该代理选择器的路由规划器
HttpRoutePlanner routePlanner = new SystemDefaultRoutePlanner(proxySelector);
// 3. 构建 HttpClient
try (CloseHttpClient httpClient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build()) {
URI uri = new URIBuilder("http://httpbin.org/get").build();
org.apache.hc.client5.http.classic.methods.HttpGet httpGet = new org.apache.hc.client5.http.classic.methods.HttpGet(uri);
System.out.println("Executing request through SOCKS proxy...");
try (org.apache.hc.client5.http.impl.classic.CloseableHttpResponse response = httpClient.execute(httpGet)) {
System.out.println("Response status: " + response.getCode());
// ... 处理响应
}
}
}
// 一个简单的 ProxySelector,它总是返回我们配置的 SOCKS 代理
static class CustomProxySelector extends ProxySelector {
private final ProxySelector defaultSelector;
private final Proxy proxy;
CustomProxySelector(ProxySelector defaultSelector, Proxy proxy) {
this.defaultSelector = defaultSelector;
this.proxy = proxy;
}
@Override
public java.util.List<java.net.Proxy> select(URI uri) {
// 总是返回 SOCKS 代理
return java.util.Collections.singletonList(proxy);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
if (defaultSelector != null) {
defaultSelector.connectFailed(uri, sa, ioe);
}
}
}
}
注意:SOCKS 代理通常是无状态的,不处理 HTTP 认证,SOCKS 代理需要认证,你需要使用支持 SOCKS 认证的库(如 Netty 的 SocksAuthRequest)并结合 HttpClient 的自定义拦截器来实现,这会更复杂。
总结与最佳实践
| 场景 | 推荐方法 | 关键组件 |
|---|---|---|
| 全局、简单的 HTTP/HTTPS 代理 | SystemDefaultRoutePlanner + JVM 系统属性 |
System.setProperty(), SystemDefaultRoutePlanner |
| 全局、需要认证的代理 | CredentialsProvider + ProxyAuthenticationStrategy |
BasicCredentialsProvider, ProxyAuthenticationStrategy |
| 为单个请求设置代理 | RequestConfig + HttpClientContext |
RequestConfig.custom().setProxy(), HttpClientContext |
| SOCKS 代理 | 自定义 ProxySelector + SystemDefaultRoutePlanner |
java.net.Proxy, 自定义 ProxySelector |
最佳实践建议:
- 优先使用
RequestConfig:除非你确实需要全局代理,否则为单个请求设置代理是更清晰、更不易出错的做法。 - 管理连接池:始终使用
try-with-resources来管理HttpClient和CloseableHttpResponse,确保资源被正确释放,在生产环境中,HttpClient实例应该是单例的,不要为每个请求都创建一个新的。 - 处理异常:网络请求充满了不确定性,务必用
try-catch块处理可能发生的IOException和其他异常。 - 消耗响应实体:在处理完
HttpEntity后,调用EntityUtils.consume(entity)或使用InputStream流式处理,以确保连接可以被正确地返回到连接池中。
