最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 中的新 HTTP 客戶端深入解析

 更新時(shí)間:2025年11月17日 08:46:36   作者:程序猿DD  
Java11引入了全新的標(biāo)準(zhǔn)化HTTP客戶端API,支持HTTP/2、異步請(qǐng)求和WebSocket,旨在替代舊的HttpURLConnection,提供更高效、更現(xiàn)代的網(wǎng)絡(luò)編程體驗(yàn),本文給大家介紹Java 中的新 HTTP 客戶端,感興趣的朋友跟隨小編一起看看吧

你是否也遇到過(guò)這樣的時(shí)刻:只是想發(fā)個(gè) HTTP 請(qǐng)求,卻被連接管理、重定向、超時(shí)與線程阻塞折騰得不亦樂(lè)乎?那就試試 Java 11 正式標(biāo)準(zhǔn)化了全新的 HttpClient,原生支持 HTTP/2、異步與 WebSocket,極大簡(jiǎn)化了客戶端網(wǎng)絡(luò)編程。

1. 概覽

本文將介紹 Java 11 對(duì)全新 HTTP 客戶端 API(支持 HTTP/2 與 WebSocket) 的標(biāo)準(zhǔn)化。

它旨在替代 JDK 早期就存在的舊類(lèi) HttpURLConnection(文檔見(jiàn):https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/HttpURLConnection.html)。

在不久之前,Java 只有較為底層、功能有限且不夠友好的 HttpURLConnection API。因此社區(qū)普遍使用第三方庫(kù),如 Apache HttpClientJetty 以及 Spring 的 RestTemplate。

2. 背景

該變更由 JEP 321 引入并最終在 Java 11 中定型。

2.1. JEP 321 的主要變更

  • Java 9 的孵化版 HTTP API 已正式并入 Java SE API。新的 HTTP APIs 位于 java.net.http.*
  • 新版本的 HTTP 協(xié)議旨在提升客戶端請(qǐng)求與服務(wù)器響應(yīng)的整體性能,包括多路復(fù)用、頭壓縮與推送承諾(push promise)等特性。
  • 自 Java 11 起,API 全面支持異步(相比之下,舊的 HTTP/1.1 實(shí)現(xiàn)是阻塞式的)。異步以 CompletableFuture 實(shí)現(xiàn),階段式流水線在前一階段完成后自動(dòng)銜接執(zhí)行。
  • 新的 HTTP 客戶端提供了標(biāo)準(zhǔn)方式執(zhí)行網(wǎng)絡(luò)操作,原生支持現(xiàn)代 Web 能力(如 HTTP/2),無(wú)需引入第三方依賴。
  • 新 API 原生支持 HTTP/1.1 與 HTTP/2 的 WebSocket。核心類(lèi)型包括:
    • HttpClientjava.net.http.HttpClient
    • HttpRequestjava.net.http.HttpRequest
    • HttpResponse<T>java.net.http.HttpResponse
    • WebSocketjava.net.http.WebSocket

2.2. Java 11 之前客戶端的問(wèn)題

舊版 HttpURLConnection 及其實(shí)現(xiàn)存在諸多問(wèn)題:

  • URLConnection 為多個(gè)如今已不再使用的協(xié)議(FTP、gopher 等)而設(shè)計(jì);
  • API 早于 HTTP/1.1,抽象層級(jí)不合時(shí)宜;
  • 僅支持阻塞模式(一次請(qǐng)求/響應(yīng)占用一個(gè)線程);
  • 維護(hù)困難。

3. HTTP Client API 總覽

與 HttpURLConnection 不同,新 HTTP 客戶端同時(shí)提供同步與異步兩種請(qǐng)求機(jī)制。

API 的三大核心:

  • HttpRequest:要發(fā)送的請(qǐng)求;
  • HttpClient:跨請(qǐng)求的通用配置容器;
  • HttpResponse:請(qǐng)求的響應(yīng)結(jié)果。

下面分別展開(kāi),先從請(qǐng)求開(kāi)始。

4. HttpRequest

HttpRequest 表示將要發(fā)送的請(qǐng)求,可通過(guò) HttpRequest.newBuilder() 獲取構(gòu)建器;構(gòu)建器提供多種便捷方法配置請(qǐng)求。

注:JDK 16 新增 HttpRequest.newBuilder(HttpRequest request, BiPredicate<String,String> filter),可基于已有請(qǐng)求復(fù)制初始狀態(tài),再在構(gòu)建前做修改(如移除部分頭):

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

4.1. 設(shè)置 URI

可直接用帶 URI 的構(gòu)造方式,或在構(gòu)建器上調(diào)用 uri(URI)

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

4.2. 指定 HTTP 方法

構(gòu)建器提供以下方法:

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

一個(gè)最簡(jiǎn)單的 GET 示例:

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

常見(jiàn)的附加參數(shù)包括:HTTP 協(xié)議版本、請(qǐng)求頭與超時(shí)。

4.3. 設(shè)置協(xié)議版本

API 默認(rèn)充分利用 HTTP/2,也可顯式指定:

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

注意:若對(duì)端不支持 HTTP/2,客戶端會(huì)回退到 HTTP/1.1。

4.4. 設(shè)置請(qǐng)求頭

可用 headers(k1,v1,k2,v2,...) 一次性傳入,或多次調(diào)用 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. 設(shè)置超時(shí)

默認(rèn)無(wú)窮大??捎?nbsp;Duration 設(shè)置,超時(shí)會(huì)拋出 HttpTimeoutException

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

5. 設(shè)置請(qǐng)求體

POST(BodyPublisher)PUT(BodyPublisher) 可攜帶請(qǐng)求體(DELETE() 也支持不帶體的刪除)。常用的 BodyPublisher 工廠有:

  • HttpRequest.BodyPublishers.ofString:基于字符串;
  • HttpRequest.BodyPublishers.ofInputStream:基于輸入流(以 Supplier<InputStream> 形式延遲創(chuàng)建);
  • HttpRequest.BodyPublishers.ofByteArray:基于字節(jié)數(shù)組;
  • HttpRequest.BodyPublishers.ofFile:基于文件路徑內(nèi)容;
  • 無(wú)請(qǐng)求體:HttpRequest.BodyPublishers.noBody()。

JDK 16 新增 BodyPublishers.concat(...),可把多個(gè) publisher 的內(nèi)容順序拼接為一個(gè)請(qǐng)求體。

5.1. 字符串請(qǐng)求體

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. 輸入流請(qǐng)求體

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. 字節(jié)數(shù)組請(qǐng)求體

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. 文件請(qǐng)求體

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

所有請(qǐng)求都由 HttpClient 發(fā)送,可通過(guò) HttpClient.newBuilder() 或 HttpClient.newHttpClient() 獲取。下面看幾個(gè)常用能力。

6.1. 處理響應(yīng)體

新的 BodyHandlers 工廠提供常見(jiàn)類(lèi)型的響應(yīng)體處理器:

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

Java 11 之前:

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

現(xiàn)在可簡(jiǎn)化為:

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

6.2. 設(shè)置代理

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

6.3. 跟隨重定向策略

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

6.4. 認(rèn)證器(Authenticator)

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

6.5. 同步與異步發(fā)送

  • 同步:send(...)(阻塞直到響應(yīng)返回)
  • 異步:sendAsync(...)(立即返回 CompletableFuture<HttpResponse<T>>

同步示例:

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

異步示例:

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

批量并發(fā)請(qǐng)求:

List<URI> 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<String>> futures = targets.stream()
  .map(target -> client
    .sendAsync(
      HttpRequest.newBuilder(target).GET().build(),
      HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body))
  .collect(Collectors.toList());

6.6. 指定異步執(zhí)行器(Executor)

ExecutorService executorService = Executors.newFixedThreadPool(2);
CompletableFuture<HttpResponse<String>> response1 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandlers.ofString());
CompletableFuture<HttpResponse<String>> response2 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandlers.ofString());

默認(rèn)執(zhí)行器為 Executors.newCachedThreadPool()

6.7. CookieHandler

設(shè)置客戶端級(jí) CookieHandler

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

若允許存儲(chǔ) Cookie,可從 CookieManager 讀?。?/p>

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

7. HttpResponse

HttpResponse 表示服務(wù)端響應(yīng),核心方法:

  • statusCode():返回整型狀態(tài)碼;
  • body():返回響應(yīng)體(類(lèi)型取決于發(fā)送時(shí)的 BodyHandler)。

其他常用方法還包括 uri()、headers()、trailers() 與 version()

7.1. 響應(yīng)的 URI

由于重定向,響應(yīng)返回的 uri() 可能與請(qǐng)求不同:

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

7.2. 響應(yīng)頭

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

7.3. 響應(yīng)協(xié)議版本

即使請(qǐng)求設(shè)置為 HTTP/2,服務(wù)端也可能以 HTTP/1.1 響應(yīng),實(shí)際版本可從響應(yīng)讀?。?/p>

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .build();
HttpResponse<String> response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandlers.ofString());
assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1));

8. HTTP/2 推送承諾(Push Promise)

新的 HttpClient 通過(guò) PushPromiseHandler 支持服務(wù)端主動(dòng)推送。當(dāng)客戶端請(qǐng)求主資源時(shí),服務(wù)器可以同時(shí)“推送”額外資源,從而減少往返次數(shù)、加快頁(yè)面渲染。該能力得益于 HTTP/2 的多路復(fù)用。

如有推送承諾,將由提供的 PushPromiseHandler 處理;若傳入 null,則拒絕所有推送。

HttpClient 的重載 sendAsync 可用于處理 push promise。先定義處理器:

private static PushPromiseHandler<String> pushPromiseHandler() {
    return (HttpRequest initiatingRequest,
        HttpRequest pushPromiseRequest,
        Function<HttpResponse.BodyHandler<String>,
        CompletableFuture<HttpResponse<String>>> 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 消費(fèi)它:

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. 總結(jié)

本文探討了 Java 11 中標(biāo)準(zhǔn)化后的 HttpClient API:在保留易用性的同時(shí),引入了 HTTP/2、異步、推送承諾、代理、重定向策略、認(rèn)證器、Cookie 管理等現(xiàn)代化能力,讓 Java 的 HTTP 編程更高效、更現(xiàn)代。

到此這篇關(guān)于探索 Java 中的新 HTTP 客戶端的文章就介紹到這了,更多相關(guān)Java HTTP 客戶端內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

晋宁县| 盘锦市| 河北区| 台中县| 鸡西市| 旬阳县| 喜德县| 伊春市| 永德县| 定南县| 巧家县| 鄄城县| 横峰县| 宣汉县| 天津市| 滦平县| 利津县| 宣威市| 弥渡县| 安平县| 高阳县| 清涧县| 兴海县| 天水市| 东乌珠穆沁旗| 阿合奇县| 宽城| 兴业县| 昌都县| 张家界市| 文山县| 南靖县| 徐州市| 漳州市| 龙州县| 乌兰察布市| 昭通市| 乌拉特后旗| 林西县| 保山市| 金坛市|