在上文中剖析了 HttpURLConnection的用法,功用仍是比较简单的,没有什么封装
接下来看看Apache HttpClient
是如何封装httpClient的
运用的版别
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
组成
HttpClient 5 的系统架构首要由以下几个部分组成:
- HttpCore:中心包,包含了 HTTP 协议的中心笼统和完结,定义了 HTTP 客户端和服务端的根本组件,例如恳求音讯、呼应音讯、传输层等。
- HttpClient:高档 API,封装了 HttpCore 包中的中心笼统,提供了一组简单易用的 API,以便于客户端应用程序发送 HTTP 恳求。
- HttpAsyncClient:异步 API,是基于 HttpCore 和 HttpClient 构建的异步 HTTP 客户端,能够经过异步办法完结 HTTP 恳求。
- HttpClient 和 HttpAsyncClient 都能够经过扩展进行定制和优化,能够增加阻拦器、设置衔接管理器、Cookie 管理器、认证器等。
恳求代码
GET恳求代码
String resultContent = null;
String url = "http://127.0.0.1:8081/get";
HttpGet httpGet = new HttpGet(url);
//经过工厂获取
CloseableHttpClient httpClient = HttpClients.createDefault();
//恳求
CloseableHttpResponse response = httpClient.execute(httpGet);
// Get status code
System.out.println(response.getVersion());
// HTTP/1.1
System.out.println(response.getCode());
// 200
System.out.println(response.getReasonPhrase());
// OK
HttpEntity entity = response.getEntity();
// Get response information
resultContent = EntityUtils.toString(entity);
System.out.println(resultContent);
代码剖析
创立实例
Apache HttpClient提供了一个工厂类来回来HttpClient
实例
但实际上都是经过HttpClientBuilder
去创立的,
Apache HttpClient经过构建者形式加上战略形式完结非常灵敏的装备,以完结各种不同的业务场景
经过看build()的代码,创立HttpClient
首要分为两步
public static CloseableHttpClient createDefault() {
return HttpClientBuilder.create().build();
}
第一步是初始化装备
里面许多战略形式的运用,能够完结相关的类来拓展自己的需求,能够经过HttpClient
的set办法把新的战略设置进去,其他装备也能够经过RequestConfig
设置好
ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
if (keepAliveStrategyCopy == null) {
keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
}
AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
if (targetAuthStrategyCopy == null) {
targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
}
AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
if (proxyAuthStrategyCopy == null) {
proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
}
在这里会初始化包含衔接管理器、恳求重试处理器、恳求履行器、重定向战略、认证战略、署理、SSL/TLS等装备
第二步是创立处理器链
经过组合多个处理器来构建成处理器链处理恳求
需求注意的是这里的增加顺序的办法,增加终究的履行处理器调用的是addLast()
处理器链中的每个处理器都有不同的功用,例如恳求预处理、重试、身份验证、恳求发送、呼应解析等等。在每个处理器的处理进程中,能够对恳求或呼应进行修正或扩展,以满意不同的需求
最后再把初始化好的参数传递给InternalHttpClient
回来一个HttpClient
实例
建议恳求
接下来看看恳求办法
CloseableHttpResponse response = httpClient.execute(httpGet);
首要恳求办法在InternalHttpClient#doExecute
中
首要分为三步,第一步是将各种装备填充到上下文中HttpContext
第二步,履行刚刚封装履行链
//execChain便是上一步封装好的履行链
final ClassicHttpResponse response = this.execChain.execute(ClassicRequestBuilder.copy(request).build(), scope);
履行 execute
办法,履行链中的处理器被顺次调用,每个元素都能够履行一些预处理、后处理、重试等逻辑
第三步,恳求结束后,将成果转换为CloseableHttpResponse
回来
自定义阻拦器和处理器
接下来试试加一下自定义的阻拦器和处理器
阻拦器和处理器的完结是不相同的,处理器的完结是ExecChainHandler
,阻拦器是HttpResponseInterceptor
和HttpRequestInterceptor
//履行链处理器
class MyCustomInterceptor implements ExecChainHandler {
@Override
public ClassicHttpResponse execute(ClassicHttpRequest request, ExecChain.Scope scope, ExecChain chain) throws IOException, HttpException {
System.out.println("MyCustomInterceptor-------------");
//调用下一个链
return chain.proceed(request,scope);
}
}
//呼应阻拦器
class MyCustomResponseInterceptor implements HttpResponseInterceptor {
@Override
public void process(HttpResponse response, EntityDetails entity, HttpContext context) throws HttpException, IOException {
System.out.println("MyCustomResponseInterceptor-------------");
}
}
//恳求阻拦器
class MyCustomRequestInterceptor implements HttpRequestInterceptor {
@Override
public void process(HttpRequest request, EntityDetails entity, HttpContext context) throws HttpException, IOException {
System.out.println("MyCustomRequestInterceptor-------------");
}
}
然后加入到阻拦链中,custom()
办法回来HttpClientBuilder
来支撑自定义
CloseableHttpClient httpClient = HttpClients.custom()
.addExecInterceptorLast("myCustomInterceptor", new MyCustomInterceptor())
.addRequestInterceptorFirst(new MyCustomRequestInterceptor())
.addResponseInterceptorLast(new MyCustomResponseInterceptor())
.build();
注意看日志就有输出了
阻拦器和处理器都是用于阻拦恳求和呼应的中间件,但它们在功用上有些不同:
- 阻拦器:在恳求发送前或呼应回来后对恳求或呼应进行修正,例如增加、删除、修正恳求头或呼应头、修正恳求体等。阻拦器的首要作用是阻拦恳求和呼应,对它们进行一些操作,并将它们传递给下一个阻拦器或处理器
- 处理器:用于履行实际的恳求和呼应处理,例如建立衔接、发送恳求、解析呼应等。处理器通常是在整个恳求-呼应流程中的最后一环,负责将终究的呼应成果回来给调用方
总归,阻拦器和处理器都是用于处理恳求和呼应的中间件,但它们的责任和功用略有不同
异步恳求
异步恳求的HttpAsyncClient
经过HttpAsyncClients
工厂回来
首要的流程和同步恳求差不多,包含初始化装备和初始化履行链,首要差异在履行恳求那里
因为是异步履行,需求敞开异步恳求的履行器线程池,经过httpClient.start();
办法来设置异步线程的状态,不然异步恳求将无法履行
@Override
public final void start() {
if (status.compareAndSet(Status.READY, Status.RUNNING)) {
executorService.execute(ioReactor::start);
}
}
假如没有敞开,会抛出反常
if (!isRunning()) {
throw new CancellationException("Request execution cancelled");
}
因为是异步恳求,所以恳求办法需求提供回调办法,首要完结三个办法,履行完结、失利和取消
//创立url
SimpleHttpRequest get = SimpleHttpRequest.create("GET", url);
Future<SimpleHttpResponse> future = httpClient.execute(get,
//异步回调
new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse result) {
System.out.println("completed---------------");
}
@Override
public void failed(Exception ex) {
System.out.println("failed---------------");
}
@Override
public void cancelled() {
System.out.println("cancelled---------------");
}
});
SimpleHttpResponse response = future.get();
经过future.get()
来获取异步成果,接下来看看底层是怎样完结的
//恳求
execute(SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), context, callback);
异步恳求会创立SimpleRequestProducer
和SimpleResponseConsumer
来处理恳求和呼应,execute()
也支撑咱们自己传进去
终究的恳求和数据的接收都是依靠管道,进程有点像NIO
当恳求时,会调用requestProducer.produce(channel);
把恳求数据写入channel
中,在呼应时,responseConsumer
从channel
中取得数据
源码的整一块恳求代码都是经过几个匿名函数的写法完结的,看的有点绕
建议恳求,匿名函数段代表的是RequestChannel
的恳求办法
//requestProducer的sendRequest办法
void sendRequest(RequestChannel channel, HttpContext context)
因为RequestChannel
类是一个函数式接口,所以能够经过这种办法调用
public interface RequestChannel {
//恳求办法也是叫sendRequest
void sendRequest(HttpRequest request, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException;
}
生产和消费数据的代码都在那一刻函数段中,具体的完结细节能够看一下那一段源码,终究requestProducer
也是委托RequestChannel
来建议恳求
消费完后经过一层一层的回调,终究抵达最上边自己完结的三个办法上
异步的HttpClient
也能够自定义阻拦器喝处理器,完结办法和上边的相同,处理异步处理器的完结不同
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(config)
.addExecInterceptorLast("myCustomInterceptor", new MyCustomInterceptor())
.addRequestInterceptorFirst(new MyCustomRequestInterceptor())
.addResponseInterceptorLast(new MyCustomResponseInterceptor())
.build();
运用示例
创立HttpClient
假如是同步的就运用HttpClients
工厂,异步的运用HttpAsyncClients
//回来默认的
CloseableHttpClient httpClient = HttpClients.createDefault();
假如想完结自定义装备,能够运用HttpClients.custom()
办法
根本的装备被封装在RequestConfig
类中
RequestConfig config = RequestConfig.custom()
.setConnectionRequestTimeout(3L, TimeUnit.SECONDS)
.setResponseTimeout(3L, TimeUnit.SECONDS)
.setDefaultKeepAlive(10L , TimeUnit.SECONDS)
.build();
假如还不满意,能够还能够去完结这些战略类
运用自定义装备创立httpClient
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(config)
.addExecInterceptorLast("myCustomInterceptor", new MyCustomInterceptor())
.addRequestInterceptorFirst(new MyCustomRequestInterceptor())
.addResponseInterceptorLast(new MyCustomResponseInterceptor())
.build();
GET办法恳求
String url = "http://127.0.0.1:8081/get";
List<NameValuePair> nvps = new ArrayList<>();
// GET 恳求参数
nvps.add(new BasicNameValuePair("username", "test"));
nvps.add(new BasicNameValuePair("password", "password"));
//将参数填充道url中
URI uri = new URIBuilder(new URI(url))
.addParameters(nvps)
.build();
//创立get恳求目标
HttpGet httpGet = new HttpGet(uri);
//建议恳求
CloseableHttpResponse response = httpClient.execute(httpGet);
// Get status code
System.out.println(response.getVersion()); // HTTP/1.1
System.out.println(response.getCode()); // 200
HttpEntity entity = response.getEntity();
// Get response information
String resultContent = EntityUtils.toString(entity);
System.out.println(resultContent);
POST恳求
这次将参数写到HttpEntity
里
String url = "http://127.0.0.1:8081/post";
List<NameValuePair> nvps = new ArrayList<>();
// GET 恳求参数
nvps.add(new BasicNameValuePair("username", "test"));
nvps.add(new BasicNameValuePair("password", "password"));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8);
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(formEntity);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(httpPost);
// Get status code
System.out.println(response.getVersion()); // HTTP/1.1
System.out.println(response.getCode()); // 200
HttpEntity entity = response.getEntity();
// Get response information
String resultContent = EntityUtils.toString(entity);
System.out.println(resultContent);
和GET恳求根本共同,除了用的是HttpPost
,参数能够像GET相同填充到url中,也能够运用HttpEntity
填充到恳求体里
Json恳求
String json = "{"
+ " "username": "test","
+ " "password": "password""
+ "}";
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
HttpPost post = new HttpPost("http://127.0.0.1:8081/postJson");
post.setEntity(entity);
CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(post);
// Get status code
System.out.println(response.getCode()); // 200
// Get response information
String resultContent = EntityUtils.toString(response.getEntity());
System.out.println(resultContent);
总结
和HttpURLConnection
相比,做了许多封装,功用也强壮了许多,例如衔接池、缓存、重试机制、线程池等等,而且关于恳求参数的设置更加灵敏,还封装了异步恳求、HTTPS等、自定义阻拦器和处理器等
恳求是运用了处理链的办法建议的,能够对恳求和呼应进行一系列处理,优点是能够将这些处理器封装成一个公共的类库,然后经过自己组合来满意自己的需求,还能够在恳求和呼应的不同阶段进行阻拦和修正,例如增加恳求头、修正恳求参数、解密呼应数据等,不需求在一个大类里写许多代码了,已免代码臃肿
功能方面运用了衔接池技术,能够有效地复用衔接,提高功能
不得不说是apache的项目,源码运用了包含构建者形式、战略形式、责任链形式等规划形式对整个httpClient进行了封装,学习到了