HTTP Client#
版本信息
- 最低 JDK:11(9/10 孵化
jdk.incubator.http→ 11 标准化为java.net.http) - JEP:JEP 321: HTTP Client (Standard)(孵化见 JEP 110)
为什么需要#
JDK 8 的 HttpURLConnection 是 1990 年代的 API,写一个 GET 都很啰嗦:openConnection() + 强转 + setRequestMethod + 手动读 InputStream 拼字符串 + disconnect(),配置头/超时要一个个 setRequestProperty,还不支持 HTTP/2,异步得自己开线程池包。JEP 321 把孵化过的 HTTP Client 标准化进 java.net.http:流式构建器、不可变请求对象、内置 HTTP/2(含多路复用、服务器推送)、WebSocket、同步与异步(CompletableFuture)双形态,API 干净现代。
语法#
examples/http-client/ 里真实可运行的源码(snippets 嵌入,与示例零漂移)——demo 用 JDK 内置 com.sun.net.httpserver 起本地服务,自包含、不依赖外部网络:
package com.javamodern.httpclient;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
// JEP 321(JDK 11):标准化 HTTP Client(java.net.http)
// —— 取代老旧、笨重的 HttpURLConnection,支持 HTTP/2、WebSocket、同步与异步。
public class HttpClientDemo {
/** 同步 GET:构建请求 → 阻塞发送 → 返回响应体字符串。 */
public static String getBody(HttpClient client, URI uri) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder() // (1) 构建不可变请求
.uri(uri)
.GET()
.build();
HttpResponse<String> response =
client.send(request, BodyHandlers.ofString()); // (2) 同步发送,BodyHandler 决定如何消费响应体
return response.body();
}
public static void main(String[] args) throws Exception {
// 用 JDK 内置 com.sun.net.httpserver 起一个本地服务,使 demo 自包含、不依赖外部网络
var server = com.sun.net.httpserver.HttpServer.create(
new java.net.InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/", exchange -> {
byte[] body = "Hello, Java 11!".getBytes(java.nio.charset.StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
try (var os = exchange.getResponseBody()) {
os.write(body);
}
});
server.start();
try {
URI uri = URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/");
HttpClient client = HttpClient.newHttpClient(); // HTTP/2-capable;对明文 HTTP 走 HTTP/1.1
System.out.println("body = " + getBody(client, uri));
} finally {
server.stop(0);
}
}
}
- :material-lightbulb:
HttpRequest.newBuilder().uri(...).GET().build()流式构建不可变请求;client.send(req, BodyHandlers.ofString())同步发送,BodyHandler决定响应体如何消费(字符串、文件、字节数组……)。
运行 HttpClientDemo.main 的实测输出(JDK 11,本地服务):
body = Hello, Java 11!
异步形态(无需阻塞,返回 CompletableFuture,本页略运行):
client.sendAsync(req, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
与 JDK 8 旧写法对比#
URL url = new URL("http://example.com/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
StringBuilder body = new StringBuilder();
try (var r = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while ((line = r.readLine()) != null) body.append(line);
}
conn.disconnect(); // 不支持 HTTP/2,异步要自己包线程池
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(5))
.build();
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/"))
.timeout(Duration.ofSeconds(10))
.GET().build();
HttpResponse<String> resp = client.send(req, BodyHandlers.ofString());
String body = resp.body(); // 或 client.sendAsync(...) 走 CompletableFuture
底层原理#
HttpClient 是核心、不可变且线程安全,可全局复用一个实例。HttpRequest 不可变、用构建器创建。发送分两条路:send 同步阻塞返回 HttpResponse;sendAsync 返回 CompletableFuture<HttpResponse>,回调链式组合、由 HttpClient 内部的线程池驱动。HTTP/2 下多个请求可多路复用到同一连接(HttpClient 自动连接复用与协议协商),HTTP/1.1 则按需。
graph LR
C["HttpClient(不可变、线程安全、可复用)"] -->|send 同步| R1["HttpResponse"]
C -->|sendAsync 异步| F["CompletableFuture<HttpResponse>"]
Req["HttpRequest(构建器、不可变)"] --> C
C -.HTTP/2 多路复用/协议协商.- H2["连接池"]
style C fill:#bbdefb
style Req fill:#c8e6c9
常见坑 / 最佳实践#
- 复用
HttpClient:它构建成本不低(含线程池/连接池),别每次请求newHttpClient();做成单例/字段复用。 - HTTP/2 通常要 HTTPS:浏览器与服务器普遍只在 TLS(ALPN 协商)上跑 HTTP/2;明文
http://通常回退 HTTP/1.1。.version(HTTP_2)是「偏好」,协商不成自动降级。 BodyHandler选对:ofString/ofByteArray/ofFile/ofInputStream/ofLines(流式)——大响应别一股脑ofString吃进内存。- 超时两层:
HttpClient.connectTimeout(建连)+HttpRequest.timeout(整体),都要设,否则可能挂死。 - 异步别忘错误分支:
sendAsync的CompletableFuture链补.exceptionally(...)/handle,否则异常被吞。
小结#
HttpClient 是 JDK 11 标准库里最「现代化」的一块:替换掉 HttpURLConnection 的陈旧与啰嗦,原生支持 HTTP/2、WebSocket 与异步,让 Java 自带一个拿得出手的 HTTP 客户端,无需再依赖三方库。