找回密码
 立即注册
首页 业界区 业界 探索 Java 中的新 HTTP 客户端

探索 Java 中的新 HTTP 客户端

碣滥 2025-11-14 11:15:00
你是否也遇到过这样的时刻:只是想发个 HTTP 请求,却被连接管理、重定向、超时与线程阻塞折腾得不亦乐乎?那就试试 Java 11 正式标准化了全新的 HttpClient,原生支持 HTTP/2、异步与 WebSocket,极大简化了客户端网络编程。
1. 概览

本文将介绍 Java 11 对全新 HTTP 客户端 API(支持 HTTP/2 与 WebSocket) 的标准化。
它旨在替代 JDK 早期就存在的旧类 HttpURLConnection(文档见:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/HttpURLConnection.html)。
在不久之前,Java 只有较为底层、功能有限且不够友好的 HttpURLConnection API。因此社区普遍使用第三方库,如 Apache HttpClient、Jetty 以及 Spring 的 RestTemplate。
2. 背景

该变更由 JEP 321 引入并最终在 Java 11 中定型。
2.1. JEP 321 的主要变更


  • Java 9 的孵化版 HTTP API 已正式并入 Java SE API。新的 HTTP APIs 位于 java.net.http.*。
  • 新版本的 HTTP 协议旨在提升客户端请求与服务器响应的整体性能,包括多路复用、头压缩与推送承诺(push promise)等特性。
  • 自 Java 11 起,API 全面支持异步(相比之下,旧的 HTTP/1.1 实现是阻塞式的)。异步以 CompletableFuture 实现,阶段式流水线在前一阶段完成后自动衔接执行。
  • 新的 HTTP 客户端提供了标准方式执行网络操作,原生支持现代 Web 能力(如 HTTP/2),无需引入第三方依赖。
  • 新 API 原生支持 HTTP/1.1 与 HTTP/2 的 WebSocket。核心类型包括:

    • HttpClient(java.net.http.HttpClient)
    • HttpRequest(java.net.http.HttpRequest)
    • HttpResponse(java.net.http.HttpResponse)
    • WebSocket(java.net.http.WebSocket)

2.2. Java 11 之前客户端的问题

旧版 HttpURLConnection 及其实现存在诸多问题:

  • URLConnection 为多个如今已不再使用的协议(FTP、gopher 等)而设计;
  • API 早于 HTTP/1.1,抽象层级不合时宜;
  • 仅支持阻塞模式(一次请求/响应占用一个线程);
  • 维护困难。
3. HTTP Client API 总览

与 HttpURLConnection 不同,新 HTTP 客户端同时提供同步与异步两种请求机制。
API 的三大核心:

  • HttpRequest:要发送的请求;
  • HttpClient:跨请求的通用配置容器;
  • HttpResponse:请求的响应结果。
下面分别展开,先从请求开始。
4. HttpRequest

HttpRequest 表示将要发送的请求,可通过 HttpRequest.newBuilder() 获取构建器;构建器提供多种便捷方法配置请求。
注:JDK 16 新增 HttpRequest.newBuilder(HttpRequest request, BiPredicate filter),可基于已有请求复制初始状态,再在构建前做修改(如移除部分头):
  1. HttpRequest.newBuilder(request, (name, value) -> !name.equalsIgnoreCase("Foo-Bar"));
复制代码
4.1. 设置 URI

可直接用带 URI 的构造方式,或在构建器上调用 uri(URI):
  1. HttpRequest.newBuilder(new URI("https://postman-echo.com/get"));
  2. HttpRequest.newBuilder()
  3.   .uri(new URI("https://postman-echo.com/get"));
复制代码
4.2. 指定 HTTP 方法

构建器提供以下方法:

  • GET()
  • POST(BodyPublisher body)
  • PUT(BodyPublisher body)
  • DELETE()
一个最简单的 GET 示例:
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/get"))
  3.   .GET()
  4.   .build();
复制代码
常见的附加参数包括:HTTP 协议版本、请求头与超时。
4.3. 设置协议版本

API 默认充分利用 HTTP/2,也可显式指定:
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/get"))
  3.   .version(HttpClient.Version.HTTP_2)
  4.   .GET()
  5.   .build();
复制代码
注意:若对端不支持 HTTP/2,客户端会回退到 HTTP/1.1。
4.4. 设置请求头

可用 headers(k1,v1,k2,v2,...) 一次性传入,或多次调用 header(k,v):
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/get"))
  3.   .headers("key1", "value1", "key2", "value2")
  4.   .GET()
  5.   .build();
  6. HttpRequest request2 = HttpRequest.newBuilder()
  7.   .uri(new URI("https://postman-echo.com/get"))
  8.   .header("key1", "value1")
  9.   .header("key2", "value2")
  10.   .GET()
  11.   .build();
复制代码
4.5. 设置超时

默认无穷大。可用 Duration 设置,超时会抛出 HttpTimeoutException:
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/get"))
  3.   .timeout(Duration.ofSeconds(10))
  4.   .GET()
  5.   .build();
复制代码
5. 设置请求体

POST(BodyPublisher), PUT(BodyPublisher) 可携带请求体(DELETE() 也支持不带体的删除)。常用的 BodyPublisher 工厂有:

  • HttpRequest.BodyPublishers.ofString:基于字符串;
  • HttpRequest.BodyPublishers.ofInputStream:基于输入流(以 Supplier 形式延迟创建);
  • HttpRequest.BodyPublishers.ofByteArray:基于字节数组;
  • HttpRequest.BodyPublishers.ofFile:基于文件路径内容;
  • 无请求体:HttpRequest.BodyPublishers.noBody()。
JDK 16 新增 BodyPublishers.concat(...),可把多个 publisher 的内容顺序拼接为一个请求体。
5.1. 字符串请求体
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/post"))
  3.   .headers("Content-Type", "text/plain;charset=UTF-8")
  4.   .POST(HttpRequest.BodyPublishers.ofString("Sample request body"))
  5.   .build();
复制代码
5.2. 输入流请求体
  1. byte[] sampleData = "Sample request body".getBytes();
  2. HttpRequest request = HttpRequest.newBuilder()
  3.   .uri(new URI("https://postman-echo.com/post"))
  4.   .headers("Content-Type", "text/plain;charset=UTF-8")
  5.   .POST(HttpRequest.BodyPublishers
  6.    .ofInputStream(() -> new ByteArrayInputStream(sampleData)))
  7.   .build();
复制代码
5.3. 字节数组请求体
  1. byte[] sampleData = "Sample request body".getBytes();
  2. HttpRequest request = HttpRequest.newBuilder()
  3.   .uri(new URI("https://postman-echo.com/post"))
  4.   .headers("Content-Type", "text/plain;charset=UTF-8")
  5.   .POST(HttpRequest.BodyPublishers.ofByteArray(sampleData))
  6.   .build();
复制代码
5.4. 文件请求体
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/post"))
  3.   .headers("Content-Type", "text/plain;charset=UTF-8")
  4.   .POST(HttpRequest.BodyPublishers.ofFile(
  5.     Paths.get("src/test/resources/sample.txt")))
  6.   .build();
复制代码
6. HttpClient

所有请求都由 HttpClient 发送,可通过 HttpClient.newBuilder() 或 HttpClient.newHttpClient() 获取。下面看几个常用能力。
6.1. 处理响应体

新的 BodyHandlers 工厂提供常见类型的响应体处理器:
  1. BodyHandlers.ofByteArray;
  2. BodyHandlers.ofString;
  3. BodyHandlers.ofFile;
  4. BodyHandlers.discarding;
  5. BodyHandlers.replacing;
  6. BodyHandlers.ofLines;
  7. BodyHandlers.fromLineSubscriber;
复制代码
Java 11 之前:
  1. HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
复制代码
现在可简化为:
  1. HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
复制代码
6.2. 设置代理
  1. HttpResponse<String> response = HttpClient
  2.   .newBuilder()
  3.   .proxy(ProxySelector.getDefault())
  4.   .build()
  5.   .send(request, BodyHandlers.ofString());
复制代码
6.3. 跟随重定向策略
  1. HttpResponse<String> response = HttpClient.newBuilder()
  2.   .followRedirects(HttpClient.Redirect.ALWAYS)
  3.   .build()
  4.   .send(request, BodyHandlers.ofString());
复制代码
6.4. 认证器(Authenticator)
  1. HttpResponse<String> response = HttpClient.newBuilder()
  2.   .authenticator(new Authenticator() {
  3.     @Override
  4.     protected PasswordAuthentication getPasswordAuthentication() {
  5.       return new PasswordAuthentication(
  6.         "username",
  7.         "password".toCharArray());
  8.     }
  9.   })
  10.   .build()
  11.   .send(request, BodyHandlers.ofString());
复制代码
6.5. 同步与异步发送


  • 同步:send(...)(阻塞直到响应返回)
  • 异步:sendAsync(...)(立即返回 CompletableFuture)
同步示例:
  1. HttpResponse<String> response = HttpClient.newBuilder()
  2.   .build()
  3.   .send(request, BodyHandlers.ofString());
复制代码
异步示例:
  1. CompletableFuture<HttpResponse<String>> response = HttpClient.newBuilder()
  2.   .build()
  3.   .sendAsync(request, HttpResponse.BodyHandlers.ofString());
复制代码
批量并发请求:
  1. List<URI> targets = Arrays.asList(
  2.   new URI("https://postman-echo.com/get?foo1=bar1"),
  3.   new URI("https://postman-echo.com/get?foo2=bar2"));
  4. HttpClient client = HttpClient.newHttpClient();
  5. List<CompletableFuture<String>> futures = targets.stream()
  6.   .map(target -> client
  7.     .sendAsync(
  8.       HttpRequest.newBuilder(target).GET().build(),
  9.       HttpResponse.BodyHandlers.ofString())
  10.     .thenApply(HttpResponse::body))
  11.   .collect(Collectors.toList());
复制代码
6.6. 指定异步执行器(Executor)
  1. ExecutorService executorService = Executors.newFixedThreadPool(2);
  2. CompletableFuture<HttpResponse<String>> response1 = HttpClient.newBuilder()
  3.   .executor(executorService)
  4.   .build()
  5.   .sendAsync(request, HttpResponse.BodyHandlers.ofString());
  6. CompletableFuture<HttpResponse<String>> response2 = HttpClient.newBuilder()
  7.   .executor(executorService)
  8.   .build()
  9.   .sendAsync(request, HttpResponse.BodyHandlers.ofString());
复制代码
默认执行器为 Executors.newCachedThreadPool()。
6.7. CookieHandler

设置客户端级 CookieHandler:
  1. HttpClient.newBuilder()
  2.   .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_NONE))
  3.   .build();
复制代码
若允许存储 Cookie,可从 CookieManager 读取:
  1. ((CookieManager) httpClient.cookieHandler().get()).getCookieStore();
复制代码
7. HttpResponse

HttpResponse 表示服务端响应,核心方法:

  • statusCode():返回整型状态码;
  • body():返回响应体(类型取决于发送时的 BodyHandler)。
其他常用方法还包括 uri()、headers()、trailers() 与 version()。
7.1. 响应的 URI

由于重定向,响应返回的 uri() 可能与请求不同:
  1. assertThat(request.uri().toString(), equalTo("http://stackoverflow.com"));
  2. assertThat(response.uri().toString(), equalTo("https://stackoverflow.com/"));
复制代码
7.2. 响应头
  1. HttpResponse<String> response = HttpClient.newHttpClient()
  2.   .send(request, HttpResponse.BodyHandlers.ofString());
  3. HttpHeaders responseHeaders = response.headers();
复制代码
7.3. 响应协议版本

即使请求设置为 HTTP/2,服务端也可能以 HTTP/1.1 响应,实际版本可从响应读取:
  1. HttpRequest request = HttpRequest.newBuilder()
  2.   .uri(new URI("https://postman-echo.com/get"))
  3.   .version(HttpClient.Version.HTTP_2)
  4.   .GET()
  5.   .build();HttpResponse response = HttpClient.newHttpClient()  .send(request, HttpResponse.BodyHandlers.ofString());assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1));
复制代码
8. HTTP/2 推送承诺(Push Promise)

新的 HttpClient 通过 PushPromiseHandler 支持服务端主动推送。当客户端请求主资源时,服务器可以同时“推送”额外资源,从而减少往返次数、加快页面渲染。该能力得益于 HTTP/2 的多路复用。
如有推送承诺,将由提供的 PushPromiseHandler 处理;若传入 null,则拒绝所有推送。
HttpClient 的重载 sendAsync 可用于处理 push promise。先定义处理器:
  1. private static PushPromiseHandler<String> pushPromiseHandler() {
  2.     return (HttpRequest initiatingRequest,
  3.         HttpRequest pushPromiseRequest,
  4.         Function<HttpResponse.BodyHandler<String>,
  5.         CompletableFuture<HttpResponse<String>>> acceptor) -> {
  6.         acceptor.apply(BodyHandlers.ofString())
  7.             .thenAccept(resp -> {
  8.                 System.out.println("Pushed response: " + resp.uri() + ", headers: " + resp.headers());
  9.             });
  10.         System.out.println("Promise request: " + pushPromiseRequest.uri());
  11.         System.out.println("Promise request headers: " + pushPromiseRequest.headers());
  12.     };
  13. }
复制代码
再用 sendAsync 消费它:
  1. httpClient.sendAsync(pageRequest, BodyHandlers.ofString(), pushPromiseHandler())
  2.     .thenAccept(pageResponse -> {
  3.         System.out.println("Page response status code: " + pageResponse.statusCode());
  4.         System.out.println("Page response headers: " + pageResponse.headers());
  5.         String responseBody = pageResponse.body();
  6.         System.out.println(responseBody);
  7.     })
  8.     .join();
复制代码
9. 总结

本文探讨了 Java 11 中标准化后的 HttpClient API:在保留易用性的同时,引入了 HTTP/2、异步、推送承诺、代理、重定向策略、认证器、Cookie 管理等现代化能力,让 Java 的 HTTP 编程更高效、更现代。
更多 Java 相关内容,也可以关注我的这个分类:Java专题

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册