此文是系列文章第十篇,前几篇请点击链接查看
程序猿的福音 - Apache Commons简介
程序员的福音 - Apache Commons Lang
程序员的福音 - Apache Commons IO
程序员的福音 - Apache Commons Codec
程序员的福音 - Apache Commons Compress
程序员的福音 - Apache Commons Exec
程序员的福音 - Apache Commons Email
程序员的福音 - Apache Commons Net
程序员的福音 - Apache Commons Collections
Apache HttpClient 组件是为扩展而设计的,同时提供对基本HTTP协议的强大支持。
java.net包提供了通过HTTP访问资源的基本功能,但它并没有提供许多应用程序所需的全部灵活性或功能。HttpClient 组件通过提供一个高效、最新、功能丰富的包来填补这一空白,该包实现了最新HTTP标准的客户端。
HttpClient 过去是 Commons 的一部分,现在是 Apache HttpComponents 的一部分。Apache HttpComponents 是 Apache 的顶级项目,负责创建和维护专注于 HTTP 和相关协议的 Java 组件工具集。因此文章后面将不再使用 Commons HttpClient 字样,而是使用 HttpClient 。
HttpClient 目前有三个大版本,他们是不兼容的,可以同时存在。HttpClient 3过去是 Commons 的一部分,所以一般来说看到 Apache HttpClient 3 的说法指的就是 Commons HttpClient,所属包 org.apache.commons.httpclient,maven 依赖如下
commons-httpclient
commons-httpclient
3.1
HttpClient 4 指的是 Apache HttpComponents 下的项目,所属包 org.apache.http,maven 依赖如下
org.apache.httpcomponents
httpclient
4.5.13
HttpClient 5 指的是 Apache HttpComponents 下的最新项目,包结构是 org.apache.hc,依赖如下
org.apache.httpcomponents.client5
httpclient5
5.1
HttpClient 3 早已不在维护,推荐使用最新的HttpClient 5,截止本文发布时间 2021-08-13,HttpClient 5 在前5天还发布了新版本。HttpClient 5 支持(经典API)(异步API)(反应式API)。
HttpClient 目前最新版本是 5.1,最低要求 Java8 以上。下面我将简单介绍下这几个版本 HttpClient 的用法。
01. 原生API
我们先来看看如果不使用 HttpClient 而是使用 Java 原生 API,写一个 http 请求的例子
HttpsURLConnection conn = null;
try {
URL url = new URL("https://www.baidu.com/");
conn = (HttpsURLConnection) url.openConnection();
// https请求需要设置证书,为了简单此处默认信任服务器不做证书校验
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new java.security.SecureRandom());
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier((s, sslSession) -> true);
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setUseCaches(false);
conn.connect();
InputStream is = conn.getInputStream();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(is))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
sb.deleteCharAt(sb.length() - 1);
println(sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
我们看到这个例子是一个相对比较简单的 https 的 get请求,没有参数。代码已经比较复杂了,如果是 post 请求,需要传递参数,需要保存cookie(有些请求需求登录,我们还要先模拟登录请求后手动将 cookie 保存下来,下次请求在把 cookie 设置上)等场景代码将更为复杂。并且原生 API 默认不支持异步不支持响应式等,这时候就轮到 HttpClient 大显手身了。
02. HttpClient 3
HttpClient 3 由于早已不在维护,不支持 http2 和异步等特性,此处只做一个最简单的示例给大家了解一下。注意所属包是 org.apache.commons.httpclient
// httpClient对象是线程安全的,可以单例使用,提升性能
HttpClient httpClient = new HttpClient();
// 设置连接超时 和 socket超时
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(2000);
httpClient.getHttpConnectionManager().getParams().setSoTimeout(5000); // 响应超时
HttpMethod getM = new GetMethod("http://test.com/");
// 设置请求头
getM.setRequestHeader("Content-Type", "application/json");
NameValuePair p1 = new NameValuePair("name", "zs");
NameValuePair p2 = new NameValuePair("age", "11");
// 设置查询参数,相当于 ?name=zs&age=11
getM.setQueryString(new NameValuePair[]{p1, p2});
try {
int code = httpClient.executeMethod(getM);
if (code == HttpStatus.SC_OK) {
// 获取结果字符串
String res = getM.getResponseBodyAsString();
// InputStream res = getM.getResponseBodyAsStream(); // 也可以转换为流
System.out.println(res);
} else {
System.err.println("请求失败,状态码:" + code);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放连接资源
getM.releaseConnection();
}
03. HttpClient 4
HttpClient 4 也不是最新版本,此处也只做一个最简单的示例给大家了解一下。注意所属包是 org.apache.http
// httpClient对象是线程安全的,可以单例使用,提升性能
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet("http://test.com/?name=zs&age=11");
httpGet.setHeader("Content-Type", "application/json");
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(2000) // 连接超时
.setConnectionRequestTimeout(2000) // 请求超时
.setSocketTimeout(2000) // 响应超时
.build();
httpGet.setConfig(requestConfig);
try (CloseableHttpResponse res = httpClient.execute(httpGet)) {
int code = res.getStatusLine().getStatusCode();
if (code == HttpStatus.SC_OK) {
HttpEntity entity = res.getEntity();
println(EntityUtils.toString(entity, "UTF-8"));
} else {
System.err.println("请求失败,状态码:" + code);
}
} catch (IOException e) {
System.err.println("请求异常");
} finally {
httpGet.reset();
// httpGet.releaseConnection(); // 等价于reset()
}
04. HttpClient 5
HttpClient 5 是目前最新版本。下面将着重介绍下。注意所属包是 org.apache.hc。HttpClient 5 除了包名和 HttpClient 4 有所区别,API的用法大体类似,只有小部分不一致。
建议将 httpClient 作为单例使用,所有请求共用一个 httpClient 对象,这样可以保存 HTTP 的状态,比如登录状态等
1. post请求
// httpClient对象是线程安全的,可以单例使用,提升性能
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("https://test.com/");
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(2))
.setConnectionRequestTimeout(Timeout.ofSeconds(2))
.setResponseTimeout(Timeout.ofSeconds(2))
.build();
httpPost.setConfig(requestConfig);
httpPost.setVersion(HttpVersion.HTTP_2); // 支持http2
httpPost.setHeader("Content-Type", "application/json");
// 支持多种entity参数,字节数组,流,文件等等
// 此处使用restful的"application/json",所以传递json字符串
httpPost.setEntity(new StringEntity(JSON.toJSONString(paramsObj)));
try (CloseableHttpResponse res = httpClient.execute(httpPost)) {
if (res.getCode() == HttpStatus.SC_OK) {
HttpEntity entity = res.getEntity();
println(EntityUtils.toString(entity));
} else {
System.err.println("请求失败,状态码:" + res.getCode());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放连接资源
httpPost.reset();
}
2. 重定向支持
有些服务器在可能会返回一个重定向的响应,状态码为302或301,如果是浏览器则会自动向重定向地址发起http请求,HttpClient 5 同样支持此功能,示例如下
CloseableHttpClient httpClient = HttpClients.custom()
.disableAutomaticRetries() //关闭自动重试
.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
.build();
// ... ...
3. 异步支持
异步 API 的好处就不说了,HttpClient 5 异步 API 使用 NIO,可以使用少量的线程支持大量的并发,在并发量较大的情况下依然可以保持服务的稳定性和吞吐量。下面看一个简单的例子
CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET.name(), "https://www.baidu.com/");
httpClient.start();
httpClient.execute(request, new FutureCallback() {
@Override
public void completed(SimpleHttpResponse result) {
// 响应成功
println(result.getBodyText());
IOUtils.closeQuietly(httpClient);
}
@Override
public void failed(Exception ex) {
// 响应出错
ex.printStackTrace();
IOUtils.closeQuietly(httpClient);
}
@Override
public void cancelled() {
// 响应取消
println("cancelled");
IOUtils.closeQuietly(httpClient);
}
});
// ... ... 做其他业务处理
05. 总结
HttpClient 作为 Java HTTP 客户端的工具类,API简单易懂,大大简化了Java HTTP 发送程序的复杂度,并且自动支持 Cookie 功能,GZip 解析,重定向支持,异步支持等等,用来代替 Java 自带的 API 是个不错的选择。
后续章节我将继续给大家介绍 commons 中其他好用的工具类库,期待你的关注。