1. HttpClient简介
HttpClient 是 Apache Jakarta Common 下的子项目,是一个用来跨项目访问的中间件 ,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HttpClient 相比传统 JDK 自带的 URLConnection(URLConnection是一个抽象类,表示指向URL指定资源的活动连接。),增加了易用性和灵活性,它不仅使客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 HTTP 协议的),即提高了开发的效率,也方便提高代码的实用性。
2.HttpClient的运用场景
- 网页内容抓取
- 模拟登录
- 接口调用
一些通过人工操作比较简单,但是比较琐碎的内容,都可以通过系统操作,就能使用httpclient了
3.HttpClient主要功能
- 实现了所有HTTP的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
- 支持HTTPS协议
- 支持代理服务器(NgInx等)等
- 支持自动(跳转)转向
- 连接管理器支持多线程应用.(支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。)
- 设置连接超时的能力。
- …
4.HttpClient使用流程
- 创建HttpClient对象.
- 创建请求方法的实例,并指定请求URL。如果需要GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
- 如果需要发送请求参数,可以调用HttpGet,HttpPost共同的setParams(HttpParams params)方法来添加请求参数;对于HttpPost对象而言,也可以调用setEntity(HttpEntity entity) 方法来设置请求参数。
- 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
- .调用 HttpResponse 的 getAllHeaders()、getHeaders(String name) 等方法可获取服务器的响应头;调用 HttpResponse 的 getEntity() 方法可获取 HttpEntity 对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
- 释放连接。无论执行方法是否成功,都必须释放连接。
5.HTTP请求的GET和POST方式的区别
- get是从服务器上获取数据,post是想服务器传送数据。
- get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
- 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务端用Request。Form获取提交的数据。
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4最大量为80KB,IIS5中为100KB。
- get安全性非常低,post安全性较高,但是执行效率却比post方法好。
6.HttpClient方法
6.1:EntityUtils()
在httpclient请求的时候,使用EntityUtils对返回的结果进行字符串的转换。
6.2:getStatusLine().getStatusCode()
获取响应的状态 状态码
6.3 : getEntity()
获取数据 包装了服务器的响应内容
6.4 : setParams(HttpParams params)
添加请求参数。
6.5:setEntity(HttpEntity entity)
设置请求参数。
6.6:execute(HttpUriRequest request)
执行请求。
7.HttpClient使用实例思路
- 创建一个httpClient对象实例(可以是从池中获取一个实例)。
- 创建一个HttpGet或HttpPost请求,传入指定的url地址。
- 调用execute方法调用传入创建的get或post请求。
- 执行rxrcute方法,会返回一个HttpResponse实例,里面是返回的数据。
- 返回内容和相应结果 200表示访问成功。
- 调用getEntity方法获取到一个HttpEntity实例里面是返回的数据。
- 调用EntityUtils.toString()将返回的数据进行字符串的转换。
- 最后无论无论访问是否成功 都要关流。
引入相关Pom坐标
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
<dependency>
频繁的创建和关闭会影响效率所以这里所引用的是池
httpClient配置文件
#最大连接数
http.maxTotal = 1000
#并发数
http.defaultMaxPerRoute = 20
#创建连接的最长时间
http.connectTimeout=5000
#从连接池中获取到连接的最长时间
http.connectionRequestTimeout=500
#数据传输的最长时间
http.socketTimeout=5000
#提交请求前测试连接是否可用
http.staleConnectionCheckEnabled=true
设置指定的参数
package com.jt.config;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource(value="classpath:/properties/httpClient.properties")
public class HttpClientConfig {
@Value("${http.maxTotal}")
private Integer maxTotal; //最大连接数
@Value("${http.defaultMaxPerRoute}")
private Integer defaultMaxPerRoute; //最大并发链接数
@Value("${http.connectTimeout}")
private Integer connectTimeout; //创建链接的最大时间
@Value("${http.connectionRequestTimeout}")
private Integer connectionRequestTimeout; //链接获取超时时间
@Value("${http.socketTimeout}")
private Integer socketTimeout; //数据传输最长时间
@Value("${http.staleConnectionCheckEnabled}")
private boolean staleConnectionCheckEnabled; //提交时检查链接是否可用
//定义httpClient链接池
@Bean(name="httpClientConnectionManager")
public PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(maxTotal); //设定最大链接数
manager.setDefaultMaxPerRoute(defaultMaxPerRoute); //设定并发链接数
return manager;
}
//定义HttpClient
/**
* 实例化连接池,设置连接池管理器。
* 这里需要以参数形式注入上面实例化的连接池管理器
@Qualifier 指定bean标签进行注入
*/
@Bean(name = "httpClientBuilder")
public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){
//HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(httpClientConnectionManager);
return httpClientBuilder;
}
/**
* 注入连接池,用于获取httpClient
* @param httpClientBuilder
* @return
*/
@Bean
public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){
return httpClientBuilder.build();
}
/**
* Builder是RequestConfig的一个内部类
* 通过RequestConfig的custom方法来获取到一个Builder对象
* 设置builder的连接信息
* @return
*/
@Bean(name = "builder")
public RequestConfig.Builder getBuilder(){
RequestConfig.Builder builder = RequestConfig.custom();
return builder.setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(connectionRequestTimeout)
.setSocketTimeout(socketTimeout)
.setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
}
/**
* 使用builder构建一个RequestConfig对象
* @param builder
* @return
*/
@Bean
public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){
return builder.build();
}
}
关闭连接
package com.jt.config;
import javax.annotation.PreDestroy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component //交给spring容器管理
public class HttpClientClose extends Thread{
@Autowired
private PoolingHttpClientConnectionManager manage;
private volatile boolean shutdown; //开关 volatitle表示多线程可变数据,一个线程修改,其他线程立即修改
public HttpClientClose() {
///System.out.println("执行构造方法,实例化对象");
//线程开启启动
this.start();
}
@Override
public void run() {
try {
//如果服务没有关闭,执行线程
while(!shutdown) {
synchronized (this) {
wait(5000); //等待5秒
//System.out.println("线程开始执行,关闭超时链接");
//关闭超时的链接
PoolStats stats = manage.getTotalStats();
int av = stats.getAvailable(); //获取可用的线程数量
int pend = stats.getPending(); //获取阻塞的线程数量
int lea = stats.getLeased(); //获取当前正在使用的链接数量
int max = stats.getMax();
//System.out.println("max/"+max+": av/"+av+": pend/"+pend+": lea/"+lea);
manage.closeExpiredConnections();
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
super.run();
}
//关闭清理无效连接的线程
@PreDestroy //容器关闭时执行该方法.
public void shutdown() {
shutdown = true;
synchronized (this) {
//System.out.println("关闭全部链接!!");
notifyAll(); //全部从等待中唤醒.执行关闭操作;
}
}
}
正常Get-Demo
@Test
public void testGet() throws ClientProtocolException, Exception
{
//Url
String url = "http://www.baidu.com";
//Get
HttpGet get = new HttpGet(url);
//初始化HttpClient
HttpClient httpClient = HttpClients.createDefault();
HttpResponse execute = httpClient.execute(get);
//获取状态码信息
if(200==execute.getStatusLine().getStatusCode())
{
//转化为字符串 并指定字符集
String string = EntityUtils.toString(execute.getEntity(),"UTF-8");
System.out.println(string);
}
}
使用池,从池中获取连接
@Autowired
private CloseableHttpClient CloseableHttpClient; //从池中获取连接
@Autowired
private RequestConfig requestConfig; // 控制请求超值时间
@Test
public void test01() throws ClientProtocolException, Exception
{
String url = "http://manage.jt.com/web/item/findItemById?itemId=562379";
HttpGet get = new HttpGet(url);
//传入配置
get.setConfig(requestConfig);
HttpResponse execute = CloseableHttpClient.execute(get);
//获取状态码信息
if(200==execute.getStatusLine().getStatusCode())
{
String string = EntityUtils.toString(execute.getEntity(),"UTF-8");
System.out.println(string);
}
}
开发中封装为一个工具使用
@Autowired
private CloseableHttpClient HttpClient; //从池中获取连接
@Autowired
private RequestConfig requestConfig; // 控制请求超值时间
//封装一下 1.url 2.参数 3.字符集
public String doGet(String url,Map<String, String> params,String charset)
{
//1. 判断字符集编码是否有值.
if(StringUtils.isEmpty(charset))
charset = "utf-8"; //没有制定字符编码集,就给utf-8
//2.判断是否有参数.
if(params!=null)//有参 ?key=value&key2=value2....
{
StringBuffer uri = new StringBuffer();
uri.append(url).append("?");
for (Map.Entry<String,String> entity : params.entrySet())
{
uri.append(entity.getKey())
.append("=")
.append(entity.getValue())
.append("&");
}
//去除多余的&号
url = uri.substring(0, uri.length()-1);
}
//3. 定义请求的类型
HttpGet get = new HttpGet(url);
get.setConfig(requestConfig);
//4.发起请求获取结果
String result = null;
try {
CloseableHttpResponse response = HttpClient.execute(get);//发送 get 请求
if(response.getStatusLine().getStatusCode()==200)// 如果请求状态码正常
result = EntityUtils.toString(response.getEntity(),charset); // 转换成String JSON串
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return result;
}
提示:
如果想要自己进行测试,可以配置相关jar包,然后先启动SpringBoot项目,然后在运行相关的test方法,进行测试。
ᕦ(・ㅂ・)ᕤ 如有不当之处,欢迎指正