老背景从JDK1.1开始,JDK中就有HttpURLConnection提供网络连接的能力,但是由于实现比较老,有很多局限性。例如HttpURLConnection通过底层提供的socket连接进行通信,每个HttpURLConnection实例只能发送一次请求,然后只能通过close()释放所请求的网络资源,或者当连接断开时使用disconnect()关闭连接连接是持久的底层套接字。而它的基类URLConnection是为了支持很多协议而设计的,但是已经不再使用FTP等协议了。HttpURLConnection并不是不能用,因为它不需要依赖,在一些demo项目中偶尔会用到。但是HttpURLConnection本身太老了,很难说HttpURLConnection能够处理包含各种认证信息和各种COOKIE信息的访问请求。针对这种情况,网上各路大神提供了更高级的包,比较流行的有Apache的HttpClient、OkhttpClient、SpringCloudFeign等。这些包提供了更丰富的资源和更方便的封装,也支持HTTP/2协议、异步请求等更高级的功能。不过到了JDK9,Java提供了一个新的Http请求工具HttpClient,再次被预览JDK10,终于在JAVA11中作为官方函数提供,也完全替代了只有阻塞模式的HttpURLConnection。HttpClient简介作为JDK11正式推出的一个新的Http连接器,支持的功能比较新。主要特点是:全面支持HTTP2.0或HTTP1.1,支持HTTPS/TLS,拦截方式简单。支持异步发送和异步时间通知。支持WebSocket支持ResponsiveStreamHTTP2.0其他客户端也可以支持,HttpClient使用CompletableFuture作为异步返回数据。WebSocket的支持是HttpClient的优势。反应流支持是HttpClient的一大优势。HttpClient中的NIO模型、函数式编程、CompletableFuture异步回调、响应式流,让HttpClient具有极强的并发处理能力,因此性能极高,内存占用更少。HttpClient的主要类有:java.net.http.HttpClientjava.net.http.HttpRequestjava.net.http.HttpResponsejava.net.http.WebSocket(本文不介绍这个)细节后面会介绍,但是WebSocket使用相对很少,本文略过。使用HttpClient的核心类主要有HttpClient、HttpRequest、HttpResponse,它们都位于java.net.http包下。接下来,我们将介绍它们。HttpClientHttpClient类是核心类,支持使用构建器模式构建复杂对象。主要参数有:Http协议的版本(HTTP1.1或HTTP2.0),默认为2.0。是否遵循服务器下发的重定向连接超时Proxy认证//可以通过参数调整HttpClientclient=HttpClient.newBuilder().version(Version.HTTP_1_1).followRedirects(Redirect.NORMAL).connectTimeout(Duration.ofSeconds(20)).proxy(ProxySelector.of(newInetSocketAddress("proxy.example.com",8080))).authenticator(Authenticator.getDefault()).build();//你也可以直接创建所有默认的HttpClientclientSimple=HttpClient.newHttpClient();HttpClient实例创建后,可以通过它发送多个请求,无需重复创建。HttpRequestHttpRequest是一个用术语描述请求体的类,也支持通过builder模式构建复杂对象。主要参数有:请求地址请求方法:GET、POST、DELETE等(默认为GET)请求正文(根据需要设置,如不使用GET)正文,但需要设置POST)请求超时时间(默认)请求头//使用参数组合构建对象,读取文件作为请求体HttpRequestrequest=HttpRequest.newBuilder().uri(URI.create("http://www.baidu.com")).timeout(Duration.ofSeconds(20)).header("Content-type","application/json").POST(HttpRequest.BodyPublishers.ofFile(Paths.get("data.json"))).build();//直接GET获取HttpRequestrequestSimple=HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();HttpRequest是一个不可变类,可以多次发送。HttpResponseHttpResponse没有提供可以在外部创建的实现类。它是根据客户端的返回值创建的接口。接口中的主要方法有:publicinterfaceHttpResponse{publicintstatusCode();公共HttpRequest请求();publicOptional>previousResponse();公共HttpHeaders标头();公共T体();公共URIuri();公共可选sslSession();publicHttpClient.Version版本();HttpResponse返回的内容符合常识,这里不再介绍。信息发送在HttpClient中可以同步发送也可以异步发送。synchronoussend()同步发送后,请求会被阻塞,直到收到响应。finalHttpResponsesend=client.send(httpRequest,HttpResponse.BodyHandlers.ofString());System.out.println(send.body());其中send的第二个参数是通过HttpResponse.BodyHandlers的静态工厂返回一个将响应转换为目标类型T的处理程序,在本例中为String。HttpResponse.BodyHandlers.ofString()的实现方法为:publicstaticBodyHandlerofString(){return(responseInfo)->BodySubscribers.ofString(charsetFrom(responseInfo.headers()));}其中,BodySubscribers.ofString()方法实现为:publicstaticBodySubscriberofString(Charsetcharset){Objects.requireNonNull(charset);returnnewResponseSubscribers.ByteArraySubscriber<>(bytes->newString(bytes,charset));}可以看到最后返回的是一个ResponseSubscribers,Subscribers就是我们之前在《JDK9响应式编程》讨论的订阅者。该构造函数的入参Function定义了订阅者中的finisher属性,当反应流完成订阅时,该属性将在onComplete()`方法中被调用。异步sendAsync()会在异步请求发送后立即返回CompletableFuture,然后可以使用CompletableFuture中的方法设置异步处理器。client.sendAsync(httpRequest,HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body).thenAccept(System.out::println).join();就像JDK中响应流中发布者的submit()方法和offer()方法是一样的。HttpClient中的send()方法是sendAsync方法的特例。在send()方法中,先调用sendAsync()方法,然后直接阻塞等待响应结束再返回。部分核心代码是:@OverridepublicHttpResponsesend(HttpRequestreq,BodyHandlerresponseHandler)throwsIOException,InterruptedException{CompletableFuture>cf=null;//如果线程已经被中断,就不需要再继续了。//cf.get()无论如何都会抛出。如果(Thread.interrupted())抛出新的InterruptedException();try{cf=sendAsync(req,responseHandler,null,null);返回cf.get();}catch(InterruptedExceptionie){if(cf!=null)cf.cancel(true);扔即;}...反应流HttpClient作为Request的发布者,向服务器发布Request,作为Response的订阅者,接收来自服务器的Response。我们在上面的send()部分发现,调用链的底端返回了一个ResponseSubscribers订阅者。当然,和HttpResponse.BodyHandlers.ofString()一样,HttpClient默认提供了一系列默认订阅者来处理数据转换:::ofFile(Path)HttpRequest.BodyPublishers::ofString(String)HttpRequest.BodyPublishers::ofInputStream(供应商)HttpResponse.BodyHandlers::ofByteArray()HttpResponse.BodyHandlers::ofString()HttpResponse.BodyHandlersofFile(Path)HttpResponse.BodyHandlers::discarding()那么在HttpClient中,我们也可以创建一个实现了Flow.Subscriber>接口的订阅者来消费数据。响应式流完整的简单的例子如下:publicclassHttpClientTest{publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{finalHttpClientclient=HttpClient.newHttpClient();finalHttpRequesthttpRequest=HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();HttpResponse.BodySubscribersubscriber=HttpResponse.BodySubscribers.fromSubscriber(newStringSubscriber(),StringSubscriber::getBody);client.sendAsync(httpRequest,responseInfo->subscriber).thenApply(HttpResponse::body).thenAccept(System.out::println).join();}静态类StringSubscriber实现Flow.Subscriber>{Flow.Subscription订阅;Listresponse=newArrayList<>();串体;publicStringgetBody(){返回正文;}@OverridepublicvoidonSubscribe(Flow.Subscriptionsubscription){this.subscription=subscription;订阅请求(1);}@OverridepublicvoidonNext(Listitem){response.addAll(item);订阅请求(1);}@OverridepublicvoidonError(Throwablethrowable){System.err.println(throwable);}@OverridepublicvoidonComplete(){byte[]data=newbyte[response.stream().mapToInt(ByteBuffer::remaining).sum()];int偏移量=0;for(ByteBufferbuffer:response){intremain=buffer.remaining();buffer.get(数据,偏移量,剩余);偏移量+=保留;}body=newString(数据);}}}最后,HttpClient是JDK11正式推出的高性能Http客户端。它的底层基于响应式流。它还通过上层封装提供异步信息发送、同步信息发送等完成的HTTP协议内容。在响应式编程方面,HttpClient也是一个很好的参考对象。