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)的更多相关文章

  1. 工具篇:apache-httpClient 和 jdk11-HttpClient的使用

    关注公众号,一起交流,微信搜一搜: 潜行前行 HttpClient (apache) apache HttpClient 是 java项目里 较为常用的组件之一:对接外部服务时,各个商家提供的接口是各 ...

  2. JDK10、JDK11、JDK12新特性

    JDK10新特性 1.var声明变量 很多人抱怨Java是一种强类型,需要引入大量的样板代码.甚至在这些情况下,给定好变量名,通常很清楚发生了什么,明显类型声明往往被认为是不必要的.许多流行的编程语言 ...

  3. linux18.04下安装的jdk11.0.2

    1.百度搜索jdk,选择jdk11.0.2,操作如下图: 2.下载完成,ctrl+alt+t打开终端并在/usr/local创建java文件夹 cd /usr/local sudo mkdir /us ...

  4. [Jenkins]JDK版本过高导致的java.io.IOException: Remote call on xxxx failed

    ------------------------------------------------------ 如需转载,请注明出处. 文章链接:https://www.cnblogs.com/dzbl ...

  5. 新手上路之JDK11的下载、安装与PATH环境变量的配置

    目录 JDK11的下载 找到目标JDK JDK的下载 JDK11的安装 PATH环境变量的配置 为什么要配置环境变量? 配置前再检查一遍 配置变量 检查环境变量配置成功与否 细究起来,JDK11与JD ...

  6. Java5~11新特性

    Java5~11版本新特性 Java5 Java6 Java7 Java8 Java9 Java10 Java11 Java5 Java5开发代号为Tiger(老虎),于2004-09-30发行 特性 ...

  7. java5-11各个版本新特性

    转载:https://blog.csdn.net/zl1zl2zl3/article/details/85055948 Java 5 Java5开发代号为Tiger(老虎),于2004-09-30发行 ...

  8. Java SE 9 新增特性

    Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...

  9. Java SE 11 新增特性

    Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

随机推荐

  1. vivo全球商城-营销价格监控方案的探索

    一.背景 现在日常官网商城的运营中有一定概率出现以下两个问题: 1)优惠信息未对齐 官网商城促销优惠的类型越来越多,能影响最终用户实付价的优惠就有抢购.满减.优惠券.代金券等.实际业务操作中存在不同促 ...

  2. P4091-[HEOI2016/TJOI2016]求和【斯特林数,NTT】

    正题 题目链接:https://www.luogu.com.cn/problem/P4091 题目大意 给出\(n\),求 \[\sum_{i=0}^n\sum_{j=0}^i\begin{Bmatr ...

  3. yolov5实战之二维码检测

    目录 1.前沿 2.二维码数据 3.训练配置 3.1数据集设置 3.2训练参数的配置 3.3网络结构设置 3.4训练 3.5结果示例 附录:数据集下载 1.前沿 之前总结过yolov5来做皮卡丘的检测 ...

  4. Java类加载器概述

    Java类加载器概述 Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由Java 应用开发人员编写的. 系统提供的类加载器 引导类加载器 它用来加载 Java 的核心库,是用原生 ...

  5. IO之字节流

    什么是字节流 像操作 图片 视频 mp4 文档(里面可能有图片) 等等 注意点 必须使用try catch finally 来包 不用throws(流是要关闭的 如果中途抛错 throws 无法将流关 ...

  6. docker-compose 搭建mongo集群

    创建目录 在每台机器上操作此步骤 一.在编写容器文件之前的注意事项: 1.yaml文件的指令前端不能使用tab键只能使用空格 2.storage: 指令的对接只能使用 : 不能使用 = 冒号的后面要跟 ...

  7. Java编译运行环境讨论(复古但能加深对Java项目的理解)

    Java编译运行环境讨论(复古但能加深对Java项目的理解) 如今我们大多数情况都会使用IDE来进行Java项目的开发,而一个如今众多优秀的IDE已经能够帮助我们自动的部署并调试运行我们的Java程序 ...

  8. Kubernetes List-Watch 机制原理与实现 - chunked

    概述http chunkedwatch api 概述 Kubernetes 中主要通过 List-Watch 机制实现组件间的异步消息通信,List-Watch 机制的实现原理值得深入分析下 . 在 ...

  9. Java(14)面向对象之封装

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201610.html 博客主页:https://www.cnblogs.com/testero ...

  10. perl 不支持多条件比较

    perl 不支持多条件比较,if(a < $var < b),这个条件表达式在C语言里面是支持的,但是在Perl中必须写成if(($var > a)&&($var & ...