探索 Java 中的新 HTTP 客户端 📅 发布时间:2026/7/15 3:02:50 👁️ 浏览次数: 你是否也遇到过这样的时刻只是想发个 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 只有较为底层、功能有限且不够友好的HttpURLConnectionAPI。因此社区普遍使用第三方库如 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。核心类型包括HttpClientjava.net.http.HttpClientHttpRequestjava.net.http.HttpRequestHttpResponseTjava.net.http.HttpResponseWebSocketjava.net.http.WebSocket2.2. Java 11 之前客户端的问题旧版HttpURLConnection及其实现存在诸多问题URLConnection为多个如今已不再使用的协议FTP、gopher 等而设计API 早于 HTTP/1.1抽象层级不合时宜仅支持阻塞模式一次请求/响应占用一个线程维护困难。3. HTTP Client API 总览与HttpURLConnection不同新 HTTP 客户端同时提供同步与异步两种请求机制。API 的三大核心HttpRequest要发送的请求HttpClient跨请求的通用配置容器HttpResponse请求的响应结果。下面分别展开先从请求开始。4. HttpRequestHttpRequest表示将要发送的请求可通过HttpRequest.newBuilder()获取构建器构建器提供多种便捷方法配置请求。注JDK 16 新增HttpRequest.newBuilder(HttpRequest request, BiPredicateString,String 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设置超时会抛出HttpTimeoutExceptionHttpRequest 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基于输入流以SupplierInputStream形式延迟创建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;charsetUTF-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;charsetUTF-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;charsetUTF-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;charsetUTF-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 之前HttpResponseString response client.send(request, HttpResponse.BodyHandler.asString());现在可简化为HttpResponseString response client.send(request, BodyHandlers.ofString());6.2. 设置代理HttpResponseString response HttpClient .newBuilder() .proxy(ProxySelector.getDefault()) .build() .send(request, BodyHandlers.ofString());6.3. 跟随重定向策略HttpResponseString response HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .build() .send(request, BodyHandlers.ofString());6.4. 认证器AuthenticatorHttpResponseString 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(...)立即返回CompletableFutureHttpResponseT同步示例HttpResponseString response HttpClient.newBuilder() .build() .send(request, BodyHandlers.ofString());异步示例CompletableFutureHttpResponseString response HttpClient.newBuilder() .build() .sendAsync(request, HttpResponse.BodyHandlers.ofString());批量并发请求ListURI targets Arrays.asList( new URI(https://postman-echo.com/get?foo1bar1), new URI(https://postman-echo.com/get?foo2bar2)); HttpClient client HttpClient.newHttpClient(); ListCompletableFutureString futures targets.stream() .map(target - client .sendAsync( HttpRequest.newBuilder(target).GET().build(), HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body)) .collect(Collectors.toList());6.6. 指定异步执行器ExecutorExecutorService executorService Executors.newFixedThreadPool(2); CompletableFutureHttpResponseString response1 HttpClient.newBuilder() .executor(executorService) .build() .sendAsync(request, HttpResponse.BodyHandlers.ofString()); CompletableFutureHttpResponseString response2 HttpClient.newBuilder() .executor(executorService) .build() .sendAsync(request, HttpResponse.BodyHandlers.ofString());默认执行器为Executors.newCachedThreadPool()。6.7. CookieHandler设置客户端级CookieHandlerHttpClient.newBuilder() .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_NONE)) .build();若允许存储 Cookie可从CookieManager读取((CookieManager) httpClient.cookieHandler().get()).getCookieStore();7. HttpResponseHttpResponse表示服务端响应核心方法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. 响应头HttpResponseString 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(); HttpResponseString 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 PushPromiseHandlerString pushPromiseHandler() { return (HttpRequest initiatingRequest, HttpRequest pushPromiseRequest, FunctionHttpResponse.BodyHandlerString, CompletableFutureHttpResponseString 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 中标准化后的HttpClientAPI在保留易用性的同时引入了 HTTP/2、异步、推送承诺、代理、重定向策略、认证器、Cookie 管理等现代化能力让 Java 的 HTTP 编程更高效、更现代。行业拓展分享一个面向研发人群使用的前后端分离的低代码软件——JNPF。基于 Java Boot/.Net Core双引擎它适配国产化支持主流数据库和操作系统提供五十几种高频预制组件内置了常用的后台管理系统使用场景和实用模版通过简单的拖拉拽操作开发者能够高效完成软件开发提高开发效率减少代码编写工作。JNPF基于SpringBootVue.js提供了一个适合所有水平用户的低代码学习平台无论是有经验的开发者还是编程新手都可以在这里找到适合自己的学习路径。此外JNPF支持全源码交付完全支持根据公司、项目需求、业务需求进行二次改造开发或内网部署具备多角色门户、登录认证、组织管理、角色授权、表单设计、流程设计、页面配置、报表设计、门户配置、代码生成工具等开箱即用的在线服务。
信息论与编码篇---N次拓展信道 我们接着之前通信的比喻,来聊聊N次拓展信道。如果把之前讨论的单次信道比作"一次交易",那N次拓展信道就是"长期合作"。1. 什么是N次拓展信道?想象一下,你和朋友不再是一次只敲一下墙,而是连续敲N下… 2026/7/13 10:38:00
Spark大数据处理:技术、应用与性能优化【1.2】 1.5.3 Spark在⻄班⽛电信的应⽤ ⻄班⽛电信(Telefnica,S.A.)是⻄班⽛的⼀家电信公司。这是全球第五⼤固⽹和移动通信运营商。 Telefnica成⽴于1924年。在1997年电信市场⾃由化之前, Telefnica是⻄班⽛唯⼀的电信运营商,⾄今仍占据主要的市场份额(2004年超过75%)。 ⻄… 2026/7/13 17:15:02
Django 中间件 Django 中间件 概述 在Django框架中,中间件(Middleware)是一个强大的功能,它允许开发者拦截处理请求和响应的过程。中间件可以用来执行多种任务,如处理用户认证、处理跨站点请求伪造(CSRF)保护、日志记录、压缩响应等。本文将深入探讨Django中间件的原理、配置和使用方… 2026/7/13 19:54:36
WinDbg 实战:利用 !htrace 命令精准追踪句柄泄漏源头 1. 句柄泄漏现象确认当你发现程序运行一段时间后变得异常缓慢,甚至直接崩溃时,任务管理器里那个不断攀升的"句柄数"可能就是罪魁祸首。我最近就遇到一个案例:某后台服务运行3天后,句柄数从正常的200个暴涨到2万多个&… 2026/7/15 3:02:10
Python虚拟环境克隆实战:三种方法实现离线快速复制与路径修正 1. 为什么需要克隆Python虚拟环境在Python项目开发中,虚拟环境(venv)是隔离项目依赖的黄金标准。但当你遇到以下场景时,直接复制已有环境可能比重建更高效:离线环境部署:军工单位、银行内网等涉密场所无法连… 2026/7/15 3:02:10
【电机控制】从六步换向到FOC:无刷电机核心算法演进与实战解析 1. 无刷电机控制技术演进全景图十年前我第一次拆解电动工具里的电机时,发现传统有刷电机内部满是碳粉,而隔壁产线的无刷电机却干净得像新的一样。这个直观对比完美诠释了技术迭代的本质——用电子换向取代机械换向,这场静悄悄的革命正从工业设… 2026/7/15 2:58:07
从网络、群体到市场:众智科学视角下的智能社会运行机制解析 1. 众智科学:智能社会的底层逻辑当你每天用手机叫外卖时,平台能在几分钟内将订单匹配给最合适的骑手;当你在短视频平台点赞,算法会立即推送相似内容——这些看似简单的场景背后,都隐藏着一个新兴学科的智慧:… 2026/7/15 2:56:06
RAG 落地别只接一个模型:检索增强生成的工程化与多模型选型 RAG(检索增强生成)几乎是「把大模型接进自己业务」的第一站。但很多团队上线后才发现:效果不行,往往不是检索差,而是模型选型和链路工程设计出了问题。本文梳理 RAG 工程化的关键决策,尤其是「为什么 RAG 链… 2026/7/15 2:52:03
MSP430FR211x超低功耗架构解析:从RISC CPU到六级休眠模式实战 1. 项目概述:为什么我们需要深入理解MSP430FR211x的低功耗架构?在嵌入式系统,尤其是电池供电的便携式设备领域,功耗管理从来都不是一个“锦上添花”的功能,而是决定产品成败的核心指标。从业十多年,我见过太… 2026/7/15 2:48:03
行星减速机的工作原理是什么?从齿轮运动关系到减速比计算 一、行星齿轮机构的组成 标准行星齿轮机构主要包括: 太阳轮; 行星轮; 内齿圈; 行星架。 太阳轮位于机构中心。 多个行星轮围绕太阳轮均匀布置,行星轮内侧与太阳轮外啮合,外侧与内齿圈内啮合。 行星轮通过轴… 2026/7/15 0:03:00
阅读Java开源框架源码的心得分享! 前几日闲来无事有幸看到了一位博主分享自己阅读开源框架源码的心得,看了之后也引发了我的一些深度思考。我们为什么要看源码?我们该怎么样去看源码? 其中前者那位博主描述的我觉得很全了(如下图所示),就不做… 2026/7/15 0:03:00
【LINUX】驱动 【LINUX驱动】【字符设备】【中断】【Platform】【网课 设备树】【GPIO】【PINCTRL】【INPUT】【IIC】【SPI】【网络驱动】【屏幕驱动】【一 设备树】【二 内核模块编译】【三 基本驱动框架】【四 Platform总线设备驱动框架】【五 驱动子系统】【六 综合】 2026/7/15 0:07:01
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/13 8:31:55
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/14 18:25:04
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/14 5:09:41