Java 11 新特性—全新的 HTTP 客户端 API

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

Java 9 提供了新的HTTP 客户端(HttpClient)来处理HTTP调用,但是那时还处于孵化器阶段,经历了 Java 9 、Java 10 ,终于在Java11版本正式“转正”。

为什么要提供全新的 HTTP 客户端

Java 11 引入新的 HTTP 客户端的原因是为了解决旧的HttpURLConnection类存在的多个问题和局限性。HttpURLConnection 存在如下几个局限性:

  1. 不直观的API设计HttpURLConnection 的 API 设计过于复杂,对于新手比较难上手,例如我们必须手动处理输入和输出流,以及错误处理。
  2. 不支持 HTTP/2HttpURLConnection仅支持 HTTP/1.1,不支持 HTTP/2。HTTP/2支持多路复用、服务器推送和头部压缩等多项特性,这些都能提高网络通信的效率。
  3. 性能问题HttpURLConnection 在高并发场景下性能表现不是很好,尤其是同步阻塞的特性更是性能的瓶颈。
  4. 缺乏现代Web特性的支持:随着Web技术的发展,如 WebSocket 等技术变得越来越重要,而HttpURLConnection并不支持这些。
  5. 缺乏异步处理能力:在处理HTTP请求时,HttpURLConnection不支持异步模式。
  6. 错误处理繁琐HttpURLConnection 的错误处理比较麻烦。我们需要检查特定的HTTP错误代码,并对不同的响应代码进行不同的处理。
  7. 连接管理不足HttpURLConnection 不支持连接池、不支持自动重试。
  8. 不可变对象和线程安全HttpURLConnection不是不可变的,也不是线程安全的。

基于上述几点,Java 11 引入 HttpClient 来替代HttpURLConnection。相比HttpURLConnection ,HttpClient 具有如下优势:

  1. 支持 HTTP/2HttpClient 支持 HTTP/2 协议的所有特性,包括同步和异步编程模型。
  2. 支持** WebSocket**:支持WebSocket,允许建立持久的连接,并进行全双工通信。
  3. 更简洁的APIHttpClient提供了更简洁、更易于使用的 API,我们能够非常方便地发送 HTTP 请求。
  4. 支持同步和异步:提供了同步和异步的两种模式。这意味着我们既可以等待HTTP响应,也可以使用回调机制处理HTTP响应。
  5. 链式调用:新的HttpClient 允许链式调用,使得构建和发送请求变得更简单。
  6. 更好的错误处理机制:新的HttpClient 提供了更好的错误处理机制,当HTTP请求失败时,可以通过异常机制更清晰地了解到发生了什么。

核心类介绍

HttpClient的主要类有下面三个:

  • java.net.http.HttpClient
  • java.net.http.HttpRequest
  • java.net.http.HttpResponse

HttpClient

HttpClient是用于发送请求和获取响应的客户端。它可以发送同步和异步的HTTP请求,并支持HTTP/1.1、HTTP/2 以及WebSocket。

由于HttpClient是不可变的,所以它是线程安全的,可以重用以发送多个请求。

利用HttpClient.newBuilder() 可以创建HttpClient的实例,并可以配置各种属性,如:

priority配置属性
version()指定HTTP协议的版本(HTTP/1.1或HTTP/2)
followRedirects()设置重定向策略
proxy()设置代理服务器
authenticator()设置HTTP身份验证
connectTimeout()置建立HTTP连接的超时时间
executor()设置自定义的Executor,该Executor用于异步任务
cookieHandler()设置Cookie处理器
sslParameters()设置SSL/TLS的配置
priority()设置请求的优先级

这些方法可以使用链式的方式配置在一起,一旦配置完成,调用build()方法就会创建一个新的HttpClient实例,该实例包含所有配置的设置。例如:

HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .followRedirects(HttpClient.Redirect.NEVER)
                .proxy(ProxySelector.of(new InetSocketAddress("127.0.0.1", 8808)))
                .connectTimeout(Duration.ofMinutes(1))
                .build();

HttpClient

HttpRequest

HttpRequest 表示一个HTTP请求,它也是不可变的,所以一旦创建,就不能更改它的配置和数据。使用 HttpRequest.newBuilder() 来创建 HttpRequest 实例,然后链式调用设置方法来配置请求,配置项如下:

方法名配置项
uri()设置请求的统一资源标识符 (URI)
timeout()设置请求的超时时间
header()添加单个请求头
headers()批量添加请求头
version()指定 HTTP 协议的版本
expectContinue()设置 Expect: 100-Continue 请求头
setHeader()设置特定的请求头,如认证头

HttpRequest 支持 GET、POST、DELETE 等方法,对应的方法如下:

方法名描述
GET()创建一个 GET 请求
POST(HttpRequest.BodyPublisher body)创建一个带有请求体的 POST 请求
PUT(HttpRequest.BodyPublisher body)创建一个带有请求体的 PUT 请求
DELETE()创建一个 DELETE 请求
method(String method, HttpRequest.BodyPublisher body)创建一个自定义方法的请求

下面代码是创建一个 GET 请求:

HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://www.skjava.com"))
                .version(HttpClient.Version.HTTP_2)
                .timeout(Duration.ofSeconds(10))
                .GET()
                .build();

对于需要请求体的请求,例如 POST、PUT,我们可以使用HttpRequest.BodyPublishers来提供请求体的内容。

HttpResponse

HttpResponse 表示一个 HTTP 响应,它包含HTTP响应的所有信息,包括状态码、响应头和响应体。与HttpClientHttpRequest一样,HttpResponse也是不可变的。

当我们利用 HttpClient 发送 HTTP 请求时,需要指定一个HttpResponse.BodyHandler来处理响应体。这个处理器决定了如何处理响应数据,比如将其作为String,如下:

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

HttpResponse 方法如下:

方法名描述
statusCode()获取HTTP响应的状态码
body()获取HTTP响应体
headers()获取HTTP响应头
uri()获取请求的URI
version()获取响应的HTTP协议版本
request()获取生成此响应的HttpRequest对象
previousResponse()获取重定向之前的响应,如果有的话
sslSession()获取SSL会话信息,如果在SSL连接上获得响应,则为Optional<SSLSession>
trailers()异步获取HTTP尾部头,返回CompletableFuture<HttpHeaders>

示例

HttpClient 可以使用同步发送和异步发送。

同步发送

HttpClient 提供了 send() 方法,发送请求后会一直阻塞到收到response为止。

    @Test
    public void sendTest() {
        // 构建 HttpClient 对象
        HttpClient httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)     //HTTP版本为HTTP/2
                .connectTimeout(Duration.ofSeconds(10))  // 连接超时为10秒
                .build();

        // 构建 HttpRequest 对象
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://skjava.com/article/list?page=3")) // uri
                .timeout(Duration.ofSeconds(5))     // 超时时间为 5 秒
                .GET().build();

        HttpResponse<String> response = null;
        try {
            response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                // 请求成功
                System.out.println("Response Body: " +  response.body());
            } else {
                // 请求不成功
                System.err.println("Response Status Code: " + response.statusCode());
                System.err.println("Response Body: " + response.body());
            }
        } catch (IOException e) {
            // 捕获并处理网络I/O错误
            System.err.println("IOException occurred: " + e.getMessage());
        } catch (InterruptedException e) {
            // 捕获并处理请求过程中的中断异常
            System.err.println("InterruptedException occurred: " + e.getMessage());
            // 中断当前线程
            Thread.currentThread().interrupt();
        }
    }

首先构建 HttpClient 和 HttpRequest 实例对象,调用 HttpClient 的 send() 方法发送 HTTP 同步请求,结果如下:

发送同步请求处理比较简单,我们只需要注意处理 IOException 和 InterruptedException 两个异常。

异步发送

HttpClient 提供了 sendAsync() 方法用于发送异步请求,发送请求后会立刻返回 CompletableFuture,然后我们可以使用CompletableFuture中的方法来设置异步处理器。

    @Test
    public void sendAsyncTest() {
        // 构建 HttpClient 对象
        HttpClient httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)     //HTTP版本为HTTP/2
                .connectTimeout(Duration.ofSeconds(10))  // 连接超时为10秒
                .build();

        // 构建 HttpRequest 对象
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://skjava.com/article/list?page=1")) // uri
                .timeout(Duration.ofSeconds(5))     // 超时时间为 5 秒
                .GET().build();

        // 发送异步请求
        CompletableFuture<HttpResponse<String>> futureResponse = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        // 处理响应
        futureResponse.thenApply(HttpResponse::body)    // 获取响应体
                .thenAccept(System.out::println)        // 打印响应体
                .join();                // 等待所有的操作完成
    }

注意,异步请求 sendAsync() 返回的是一个 CompletableFuture,关于 可以看这篇文章:Java 8 新特性—深入理解 CompletableFuture

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值