JAVA - HttpClient(JDK11)

Posted 任某某

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA - HttpClient(JDK11)相关的知识,希望对你有一定的参考价值。

1
HttpClient简介


  HttpClient是Apache Jakarta common下的子项目,用来提供高效的、最新的、更能丰富的支持http协议的客户端编程工具包,并且它支持http协议最新的版本和建议。

Module java.net.http
Package java.net.http

特性:
  • 以可扩展的面向对象的结构实现了HTTP全部的方法(GET、POST、put、delete、head、options、trace

  • 支持HTTPS协议

  • 通过HTTP代理建立透明的连接

  • 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接

  • 自动处理Set-Cookie中的Cookie

  • 插件式的自定义Cookie策略

  • request的输出流可以避免流中内容直接缓冲到socket服务器

  • Response的输入流可以有效的从socket服务器直接读取相应内容


在官方示例中提供了 同步请求以及异步请求的示例,如下:

// 同步 示例HttpClient client = HttpClient.newBuilder() .version(Version.HTTP_1_1) .followRedirects(Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(20)) .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))) .authenticator(Authenticator.getDefault()) .build();HttpResponse<String> response = client.send(request, BodyHandlers.ofString());System.out.println(response.statusCode());System.out.println(response.body());
// 异步 示例HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://foo.com/")) .timeout(Duration.ofMinutes(2)) .header("Content-Type", "application/json") .POST(BodyPublishers.ofFile(Paths.get("file.json"))) .build();  client.sendAsync(request, BodyHandlers.ofString())        .thenApply(HttpResponse::body)        .thenAccept(System.out::println);

 与Apache HttpClient 相比较这种创建方式更为灵活。 
  在创建HttpClient时根据自己需要进行选择性创建。


2
创建HttpClient

//创建 builderHttpClient.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();

点进build()方法的实现类,会发现调用了HttpClientImpl 的创建

@Overridepublic HttpClient build() {  return HttpClientImpl.create(this);}

HttpClientImpl中的cretae() 方法中的核心即为

HttpClientImpl impl = new HttpClientImpl(builder, facadeFactory);

 以下是 HttpClientImpl 方法的创建示例

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) { // unlikely throw new InternalError(e); } //设置为守护线程 selmgr.setDaemon(true); filters = new FilterFactory(); initFilters(); assert facadeRef.get() != null;}

以上是实际创建请求时的方法。 



3
HttpRequst是请求的主体部分


   
     
     
   
//创建 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();



4
发送请求 HttpResponse

   
     
     
   
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
以上即介绍了 HttpCilent 从创建 到HttpRequst配置 再到HttpResponse 请求,以我个人来看使用起来还是蛮方便的。
以下是我使用HttpClient请求钉钉Api的示例

String appKeySys = DingTalkConfig.APP_KEY;String appSecretSys = DingTalkConfig.APP_SECRET;//钉钉请求// DefaultDingTalkClient client = new DefaultDingTalkClient(Constant.DingTalkConfig.URL_GET_TOKKEN);// OapiGettokenRequest request = new OapiGettokenRequest();
HttpClient.Builder builder = HttpClient.newBuilder();HttpClient client = builder.version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofMillis(5000)) .followRedirects(HttpClient.Redirect.ALWAYS) .build();HttpRequest.Builder reBuilder = HttpRequest.newBuilder();
Map<String,Object> dingKeyMap = jdbcTemplate.queryForMap("select CORP_ID,SECRET from xgtz_data_factory.wechat_access_token where id = 2");if (!StringUtils.isEmpty(dingKeyMap)){ if (!StringUtils.isEmpty(dingKeyMap.get("CORP_ID")) && !StringUtils.isEmpty(dingKeyMap.get("SECRET"))){ appKeySys = TypeUtils.castToString(dingKeyMap.get("CORP_ID")); appSecretSys = TypeUtils.castToString(dingKeyMap.get("SECRET")); }}// request.setAppkey(appKeySys);// request.setAppsecret(appSecretSys);// request.setHttpMethod("GET");// OapiGettokenResponse response = client.execute(request);HttpRequest request = reBuilder.header("Content-Type", "application/json") .GET() .timeout(Duration.ofMillis(6000)) .uri(URI.create( Constant.DingTalkConfig.URL_GET_TOKKEN .concat("?appkey=").concat(appKeySys) .concat("&appsecret=").concat(appSecretSys) ))        .build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

end



请求方式根据个人实际情况选择就好了。


下一篇记录下RestTemplate

每天积累一点点,十天头顶秃一片,淦



以上是关于JAVA - HttpClient(JDK11)的主要内容,如果未能解决你的问题,请参考以下文章

JDK 之 HttpClient(jdk11)

jdk11新特性——标准Java异步HTTP客户端

jdk11新特性——标准Java异步HTTP客户端

如何在 Java JDK HttpClient 中使用 socks 代理

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

第一篇 : JDK11 介绍