【转载】JDK 之 HttpClient(jdk11)

HttpClient 简介

java.net.http.HttpClient 是 jdk11 中正式启用的一个 http 工具类(其实早在 jdk9 的时候就已经存在了,只是处于孵化期),官方寓意为想要取代 HttpURLConnection 和 Apache HttpClient 等比较古老的开发工具。

新增的 HttpClient 截止到目前(2019年3月)为止其实网络资料还比较少,笔者只是根据一些博文和官方 Demo 自己摸索了一下,做了下总结。

由于是 jdk11 中才正式使用的工具类,距离开发者还很遥远,所以对于源码笔者暂不打算深挖,浅浅的理解怎么使用就行

一、HttpClient在 Apache HttpClient 中,一般会创建一个 HttpClient 对象来作为门面。java.net.http.HttpClient 的逻辑也差不多,只是创建方式更加时髦了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//创建 builder
HttpClient.Builder builder = HttpClient.newBuilder();
 
//链式调用
HttpClient client = builder
 
//http 协议版本 1.1 或者 2
.version(HttpClient.Version.HTTP_2) //.version(HttpClient.Version.HTTP_1_1)
 
//连接超时时间,单位为毫秒
.connectTimeout(Duration.ofMillis( 5000 )) //.connectTimeout(Duration.ofMinutes(1))
 
//连接完成之后的转发策略
.followRedirects(HttpClient.Redirect.NEVER) //.followRedirects(HttpClient.Redirect.ALWAYS)
 
//指定线程池
.executor(Executors.newFixedThreadPool( 5 ))
 
//认证,默认情况下 Authenticator.getDefault() 是 null 值,会报错
//.authenticator(Authenticator.getDefault())
 
//代理地址
//.proxy(ProxySelector.of(new InetSocketAddress("http://www.baidu.com", 8080)))
 
//缓存,默认情况下 CookieHandler.getDefault() 是 null 值,会报错
//.cookieHandler(CookieHandler.getDefault())
 
//创建完成
.build();

在 builder() 方法中,最终会调用到 HttpClientImpl 的构造器,完成 HttpClient 的创建工作:

复制代码
    //HttpClientImpl.class
    private HttpClientImpl(HttpClientBuilderImpl builder,
                           SingleFacadeFactory facadeFactory) {
        //CLIENT_IDS 是 AtomicLong 类型的变量,使用 incrementAndGet() 方法实现自增长的 id id = CLIENT_IDS.incrementAndGet(); //记录下存有 id 的字符串 dbgTag = "HttpClientImpl(" + id + ")"; //ssl 认证 if (builder.sslContext == null) { try { sslContext = SSLContext.getDefault(); } catch (NoSuchAlgorithmException ex) { throw new InternalError(ex); } } else { sslContext = builder.sslContext; } //线程池,没有的话就默认创建一个 Executor ex = builder.executor; if (ex == null) { ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id)); isDefaultExecutor = true; } else { isDefaultExecutor = false; } delegatingExecutor = new DelegatingExecutor(this::isSelectorThread, ex); facadeRef = new WeakReference<>(facadeFactory.createFacade(this)); //处理 http 2 的 client 类 client2 = new Http2ClientImpl(this);‘ //缓存操作 cookieHandler = builder.cookieHandler; //超时时间 connectTimeout = builder.connectTimeout; //转发策略,默认为 NEVER followRedirects = builder.followRedirects == null ? Redirect.NEVER : builder.followRedirects; //代理设置 this.userProxySelector = Optional.ofNullable(builder.proxy); this.proxySelector = userProxySelector .orElseGet(HttpClientImpl::getDefaultProxySelector); if (debug.on()) debug.log("proxySelector is %s (user-supplied=%s)", this.proxySelector, userProxySelector.isPresent()); //认证设置 authenticator = builder.authenticator; //设置 http 协议版本 if (builder.version == null) { version = HttpClient.Version.HTTP_2; } else { version = builder.version; } if (builder.sslParams == null) { sslParams = getDefaultParams(sslContext); } else { sslParams = builder.sslParams; } //连接线程池 connections = new ConnectionPool(id); connections.start(); timeouts = new TreeSet<>(); //SelectorManager 本质上是 Thread 类的封装 //selmgr 会开启一条线程,HttpClient 的主要逻辑运行在此线程中 //所以说 HttpClient 是非阻塞的,因为并不跑在主线程中 try { selmgr = new SelectorManager(this); } catch (IOException e) { throw new InternalError(e); } //设置为守护线程 selmgr.setDaemon(true); filters = new FilterFactory(); initFilters(); assert facadeRef.get() != null; }
复制代码

主要是一些储存操作,大致理解即可,不细究。

二、HttpRequest

HttpRequest 是发起请求的主体配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//创建 builder
HttpRequest.Builder reBuilder = HttpRequest.newBuilder();
 
//链式调用
HttpRequest request = reBuilder
 
//存入消息头
//消息头是保存在一张 TreeMap 里的
.header( "Content-Type" , "application/json" )
 
//http 协议版本
.version(HttpClient.Version.HTTP_2)
 
//url 地址
.uri(URI.create( "http://openjdk.java.net/" ))
 
//超时时间
.timeout(Duration.ofMillis( 5009 ))
 
//发起一个 post 消息,需要存入一个消息体
.POST(HttpRequest.BodyPublishers.ofString( "hello" ))
 
//发起一个 get 消息,get 不需要消息体
//.GET()
 
//method(...) 方法是 POST(...) 和 GET(...) 方法的底层,效果一样
//.method("POST",HttpRequest.BodyPublishers.ofString("hello"))
 
//创建完成
.build();

三、发送

发起请求:

1
2
HttpResponse<String> response =
         client.send(request, HttpResponse.BodyHandlers.ofString());
这是同步式的发起请求方式,先来看一下它的实现:
复制代码
   public <T> HttpResponse<T> send(HttpRequest req, BodyHandler<T> responseHandler)
            throws IOException, InterruptedException{
        CompletableFuture<HttpResponse<T>> cf = null; try {  //调用 sendAsync(...) 方法异步地完成主逻辑,并获取 Future cf = sendAsync(req, responseHandler, null, null); return cf.get();       //这之后的所有代码都是在进行异常捕捉,所以可以忽略 } catch (InterruptedException ie) { if (cf != null ) cf.cancel(true); throw ie; } catch (ExecutionException e) { final Throwable throwable = e.getCause(); final String msg = throwable.getMessage(); if (throwable instanceof IllegalArgumentException) { throw new IllegalArgumentException(msg, throwable); } else if (throwable instanceof SecurityException) { throw new SecurityException(msg, throwable); } else if (throwable instanceof HttpConnectTimeoutException) { HttpConnectTimeoutException hcte = new HttpConnectTimeoutException(msg); hcte.initCause(throwable); throw hcte; } else if (throwable instanceof HttpTimeoutException) { throw new HttpTimeoutException(msg); } else if (throwable instanceof ConnectException) { ConnectException ce = new ConnectException(msg); ce.initCause(throwable); throw ce; } else if (throwable instanceof IOException) { throw new IOException(msg, throwable); } else { throw new IOException(msg, throwable); } } }
复制代码

本质上是使用了异步实现方法 sendAsync(…)。

在 Demo 中也可以直接使用:

1
2
3
4
5
6
//返回的是 future,然后通过 future 来获取结果
CompletableFuture<String> future =
         client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                 .thenApply(HttpResponse::body);
//阻塞线程,从 future 中获取结果
String body = future.get();

转载自蒋蒋蒋博客,原文链接:https://www.cnblogs.com/JiangWJ/p/10823112.html

猜你喜欢

转载自www.cnblogs.com/timhuang1995/p/12741467.html