探索 Java 中的新 HTTP 客户端

2025-12-12 0 661

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

1. 概览

本文将介绍 Java 11 对全新 HTTP 客户端 API(支持 HTTP/2 与 WebSocket) 的标准化。

它旨在替代 JDK 早期就存在的旧类 HttpURLConnection(文档见:docs.oracle.com/en/java/jav…

在不久之前,Java 只有较为底层、功能有限且不够友好的 HttpURLConnection API。因此社区普遍使用第三方库,如 Apache HttpClient、Jetty 以及 Spring 的 RestTemplate。

2. 背景

该变更由 JEP 321 引入并最终在 Java 11 中定型。

2.1. JEP 321 的主要变更

  1. Java 9 的孵化版 HTTP API 已正式并入 Java SE API。新的 HTTP APIs 位于 java.net.http.*
  2. 新版本的 HTTP 协议旨在提升客户端请求与服务器响应的整体性能,包括多路复用、头压缩与推送承诺(push promise)等特性。
  3. 自 Java 11 起,API 全面支持异步(相比之下,旧的 HTTP/1.1 实现是阻塞式的)。异步以 CompletableFuture 实现,阶段式流水线在前一阶段完成后自动衔接执行。
  4. 新的 HTTP 客户端提供了标准方式执行网络操作,原生支持现代 Web 能力(如 HTTP/2),无需引入第三方依赖。
  5. 新 API 原生支持 HTTP/1.1 与 HTTP/2 的 WebSocket。核心类型包括:
    • HttpClientjava.net.http.HttpClient
    • HttpRequestjava.net.http.HttpRequest
    • HttpResponsejava.net.http.HttpResponse
    • WebSocketjava.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),可基于已有请求复制初始状态,再在构建前做修改(如移除部分头):

HttpRequest.newBuilder(request, (name, value) -> !name.equalsIgnoreCase(\"Foo-Bar\"));

4.1. 设置 URI

可直接用带 URI 的构造方式,或在构建器上调用 uri(URI)

HttpRequest.newBuilder(new URI(\"https://postman-echo.com/get\"));

HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"));

4.2. 指定 HTTP 方法

构建器提供以下方法:

  • GET()
  • POST(BodyPublisher body)
  • PUT(BodyPublisher body)
  • DELETE()

一个最简单的 GET 示例:

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .GET()
  .build();

常见的附加参数包括:HTTP 协议版本、请求头与超时。

4.3. 设置协议版本

API 默认充分利用 HTTP/2,也可显式指定:

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .build();

注意:若对端不支持 HTTP/2,客户端会回退到 HTTP/1.1。

4.4. 设置请求头

可用 headers(k1,v1,k2,v2,...) 一次性传入,或多次调用 header(k,v)

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .headers(\"key1\", \"value1\", \"key2\", \"value2\")
  .GET()
  .build();

HttpRequest request2 = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .header(\"key1\", \"value1\")
  .header(\"key2\", \"value2\")
  .GET()
  .build();

4.5. 设置超时

默认无穷大。可用 Duration 设置,超时会抛出 HttpTimeoutException

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .timeout(Duration.ofSeconds(10))
  .GET()
  .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. 字符串请求体

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/post\"))
  .headers(\"Content-Type\", \"text/plain;charset=UTF-8\")
  .POST(HttpRequest.BodyPublishers.ofString(\"Sample request body\"))
  .build();

5.2. 输入流请求体

byte[] sampleData = \"Sample request body\".getBytes();
HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/post\"))
  .headers(\"Content-Type\", \"text/plain;charset=UTF-8\")
  .POST(HttpRequest.BodyPublishers
   .ofInputStream(() -> new ByteArrayInputStream(sampleData)))
  .build();

5.3. 字节数组请求体

byte[] sampleData = \"Sample request body\".getBytes();
HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/post\"))
  .headers(\"Content-Type\", \"text/plain;charset=UTF-8\")
  .POST(HttpRequest.BodyPublishers.ofByteArray(sampleData))
  .build();

5.4. 文件请求体

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/post\"))
  .headers(\"Content-Type\", \"text/plain;charset=UTF-8\")
  .POST(HttpRequest.BodyPublishers.ofFile(
    Paths.get(\"src/test/resources/sample.txt\")))
  .build();

6. HttpClient

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

6.1. 处理响应体

新的 BodyHandlers 工厂提供常见类型的响应体处理器:

BodyHandlers.ofByteArray;
BodyHandlers.ofString;
BodyHandlers.ofFile;
BodyHandlers.discarding;
BodyHandlers.replacing;
BodyHandlers.ofLines;
BodyHandlers.fromLineSubscriber;

Java 11 之前:

HttpResponse response = client.send(request, HttpResponse.BodyHandler.asString());

现在可简化为:

HttpResponse response = client.send(request, BodyHandlers.ofString());

6.2. 设置代理

HttpResponse response = HttpClient
  .newBuilder()
  .proxy(ProxySelector.getDefault())
  .build()
  .send(request, BodyHandlers.ofString());

6.3. 跟随重定向策略

HttpResponse response = HttpClient.newBuilder()
  .followRedirects(HttpClient.Redirect.ALWAYS)
  .build()
  .send(request, BodyHandlers.ofString());

6.4. 认证器(Authenticator)

HttpResponse response = HttpClient.newBuilder()
  .authenticator(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(
        \"username\",
        \"password\".toCharArray());
    }
  })
  .build()
  .send(request, BodyHandlers.ofString());

6.5. 同步与异步发送

  • 同步:send(...)(阻塞直到响应返回)
  • 异步:sendAsync(...)(立即返回 CompletableFuture<HttpResponse>

同步示例:

HttpResponse response = HttpClient.newBuilder()
  .build()
  .send(request, BodyHandlers.ofString());

异步示例:

CompletableFuture<HttpResponse> response = HttpClient.newBuilder()
  .build()
  .sendAsync(request, HttpResponse.BodyHandlers.ofString());

批量并发请求:

List targets = Arrays.asList(
  new URI(\"https://postman-echo.com/get?foo1=bar1\"),
  new URI(\"https://postman-echo.com/get?foo2=bar2\"));
HttpClient client = HttpClient.newHttpClient();
List<CompletableFuture> futures = targets.stream()
  .map(target -> client
    .sendAsync(
      HttpRequest.newBuilder(target).GET().build(),
      HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body))
  .collect(Collectors.toList());

6.6. 指定异步执行器(Executor)

ExecutorService executorService = Executors.newFixedThreadPool(2);

CompletableFuture<HttpResponse> response1 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandlers.ofString());

CompletableFuture<HttpResponse> response2 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandlers.ofString());

默认执行器为 Executors.newCachedThreadPool()

6.7. CookieHandler

设置客户端级 CookieHandler

HttpClient.newBuilder()
  .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_NONE))
  .build();

若允许存储 Cookie,可从 CookieManager 读取:

((CookieManager) httpClient.cookieHandler().get()).getCookieStore();

7. HttpResponse

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

  • statusCode():返回整型状态码;
  • body():返回响应体(类型取决于发送时的 BodyHandler)。

其他常用方法还包括 uri()headers()trailers()version()

7.1. 响应的 URI

由于重定向,响应返回的 uri() 可能与请求不同:

assertThat(request.uri().toString(), equalTo(\"http://stackoverflow.com\"));
assertThat(response.uri().toString(), equalTo(\"https://stackoverflow.com/\"));

7.2. 响应头

HttpResponse response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandlers.ofString());
HttpHeaders responseHeaders = response.headers();

7.3. 响应协议版本

即使请求设置为 HTTP/2,服务端也可能以 HTTP/1.1 响应,实际版本可从响应读取:

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI(\"https://postman-echo.com/get\"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .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。先定义处理器:

private static PushPromiseHandler pushPromiseHandler() {
    return (HttpRequest initiatingRequest,
        HttpRequest pushPromiseRequest,
        Function<HttpResponse.BodyHandler,
        CompletableFuture<HttpResponse>> acceptor) -> {
        acceptor.apply(BodyHandlers.ofString())
            .thenAccept(resp -> {
                System.out.println(\"Pushed response: \" + resp.uri() + \", headers: \" + resp.headers());
            });
        System.out.println(\"Promise request: \" + pushPromiseRequest.uri());
        System.out.println(\"Promise request headers: \" + pushPromiseRequest.headers());
    };
}

再用 sendAsync 消费它:

httpClient.sendAsync(pageRequest, BodyHandlers.ofString(), pushPromiseHandler())
    .thenAccept(pageResponse -> {
        System.out.println(\"Page response status code: \" + pageResponse.statusCode());
        System.out.println(\"Page response headers: \" + pageResponse.headers());
        String responseBody = pageResponse.body();
        System.out.println(responseBody);
    })
    .join();

9. 总结

本文探讨了 Java 11 中标准化后的 HttpClient API:在保留易用性的同时,引入了 HTTP/2、异步、推送承诺、代理、重定向策略、认证器、Cookie 管理等现代化能力,让 Java 的 HTTP 编程更高效、更现代。

更多 Java 相关内容,也可以关注我的这个分类:Java专题

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 探索 Java 中的新 HTTP 客户端 https://www.zuozi.net/35983.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务