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 的逻辑也差不多,只是创建方式更加时髦了:
//创建 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 是发起请求的主体配置:
//创建 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();
三、发送
发起请求:
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 中也可以直接使用:
//返回的是 future,然后通过 future 来获取结果
CompletableFuture<String> future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
//阻塞线程,从 future 中获取结果
String body = future.get();
JDK 之 HttpClient(jdk11)的更多相关文章
- 工具篇:apache-httpClient 和 jdk11-HttpClient的使用
关注公众号,一起交流,微信搜一搜: 潜行前行 HttpClient (apache) apache HttpClient 是 java项目里 较为常用的组件之一:对接外部服务时,各个商家提供的接口是各 ...
- JDK10、JDK11、JDK12新特性
JDK10新特性 1.var声明变量 很多人抱怨Java是一种强类型,需要引入大量的样板代码.甚至在这些情况下,给定好变量名,通常很清楚发生了什么,明显类型声明往往被认为是不必要的.许多流行的编程语言 ...
- linux18.04下安装的jdk11.0.2
1.百度搜索jdk,选择jdk11.0.2,操作如下图: 2.下载完成,ctrl+alt+t打开终端并在/usr/local创建java文件夹 cd /usr/local sudo mkdir /us ...
- [Jenkins]JDK版本过高导致的java.io.IOException: Remote call on xxxx failed
------------------------------------------------------ 如需转载,请注明出处. 文章链接:https://www.cnblogs.com/dzbl ...
- 新手上路之JDK11的下载、安装与PATH环境变量的配置
目录 JDK11的下载 找到目标JDK JDK的下载 JDK11的安装 PATH环境变量的配置 为什么要配置环境变量? 配置前再检查一遍 配置变量 检查环境变量配置成功与否 细究起来,JDK11与JD ...
- Java5~11新特性
Java5~11版本新特性 Java5 Java6 Java7 Java8 Java9 Java10 Java11 Java5 Java5开发代号为Tiger(老虎),于2004-09-30发行 特性 ...
- java5-11各个版本新特性
转载:https://blog.csdn.net/zl1zl2zl3/article/details/85055948 Java 5 Java5开发代号为Tiger(老虎),于2004-09-30发行 ...
- Java SE 9 新增特性
Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...
- Java SE 11 新增特性
Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
随机推荐
- vivo全球商城-营销价格监控方案的探索
一.背景 现在日常官网商城的运营中有一定概率出现以下两个问题: 1)优惠信息未对齐 官网商城促销优惠的类型越来越多,能影响最终用户实付价的优惠就有抢购.满减.优惠券.代金券等.实际业务操作中存在不同促 ...
- P4091-[HEOI2016/TJOI2016]求和【斯特林数,NTT】
正题 题目链接:https://www.luogu.com.cn/problem/P4091 题目大意 给出\(n\),求 \[\sum_{i=0}^n\sum_{j=0}^i\begin{Bmatr ...
- yolov5实战之二维码检测
目录 1.前沿 2.二维码数据 3.训练配置 3.1数据集设置 3.2训练参数的配置 3.3网络结构设置 3.4训练 3.5结果示例 附录:数据集下载 1.前沿 之前总结过yolov5来做皮卡丘的检测 ...
- Java类加载器概述
Java类加载器概述 Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由Java 应用开发人员编写的. 系统提供的类加载器 引导类加载器 它用来加载 Java 的核心库,是用原生 ...
- IO之字节流
什么是字节流 像操作 图片 视频 mp4 文档(里面可能有图片) 等等 注意点 必须使用try catch finally 来包 不用throws(流是要关闭的 如果中途抛错 throws 无法将流关 ...
- docker-compose 搭建mongo集群
创建目录 在每台机器上操作此步骤 一.在编写容器文件之前的注意事项: 1.yaml文件的指令前端不能使用tab键只能使用空格 2.storage: 指令的对接只能使用 : 不能使用 = 冒号的后面要跟 ...
- Java编译运行环境讨论(复古但能加深对Java项目的理解)
Java编译运行环境讨论(复古但能加深对Java项目的理解) 如今我们大多数情况都会使用IDE来进行Java项目的开发,而一个如今众多优秀的IDE已经能够帮助我们自动的部署并调试运行我们的Java程序 ...
- Kubernetes List-Watch 机制原理与实现 - chunked
概述http chunkedwatch api 概述 Kubernetes 中主要通过 List-Watch 机制实现组件间的异步消息通信,List-Watch 机制的实现原理值得深入分析下 . 在 ...
- Java(14)面向对象之封装
作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201610.html 博客主页:https://www.cnblogs.com/testero ...
- perl 不支持多条件比较
perl 不支持多条件比较,if(a < $var < b),这个条件表达式在C语言里面是支持的,但是在Perl中必须写成if(($var > a)&&($var & ...