知识概述
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HttpClient的主要功能:
- 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
- 支持 HTTPS 协议
- 支持代理服务器(Nginx等)等
- 支持自动(跳转)转向
HttpClient入门案例
1.添加httpClient jar包
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
2.实现类
public class TestHttpClient { /** * 1.实例化httpClient对象 * 2.准备url请求地址 https://www.baidu.com/ * 3.封装请求方式对象 GET/POST/PUT/DELETE * 4.发起http请求.获取服务器响应. * 5.判断返回值状态码信息 200. * 6.从响应对象中获取服务器返回值数据. * @throws IOException * @throws ClientProtocolException */ @Test public void testGet() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); String url = "https://www.baidu.com"; HttpGet get = new HttpGet(url); CloseableHttpResponse response = client.execute(get); if(response.getStatusLine().getStatusCode() == 200) { //表示请求服务正确 HttpEntity entity = response.getEntity();//返回值实体对象 String result = EntityUtils.toString(entity, "UTF-8"); System.out.println(result); } } }
GET无参:
HttpClient发送示例:
@Test public void doGetTestOne() { // 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 创建Get请求 HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerOne"); // 响应模型 CloseableHttpResponse response = null; try { // 由客户端执行(发送)Get请求 response = httpClient.execute(httpGet); // 从响应模型中获取响应实体 HttpEntity responseEntity = response.getEntity(); System.out.println("响应状态为:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("响应内容长度为:" + responseEntity.getContentLength()); System.out.println("响应内容为:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 释放资源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
对应接收示例:
GET有参(方式一:直接拼接URL):
HttpClient发送示例:
/** * GET---有参测试 (方式一:手动在url后面加上参数) * * @date 2018年7月13日 下午4:19:23 */ @Test public void doGetTestWayOne() { // 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 参数 StringBuffer params = new StringBuffer(); try { // 字符数据最好encoding以下;这样一来,某些特殊字符才能传过去(如:某人的名字就是“&”,不encoding的话,传不过去) params.append("name=" + URLEncoder.encode("&", "utf-8")); params.append("&"); params.append("age=24"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } // 创建Get请求 HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerTwo" + "?" + params); // 响应模型 CloseableHttpResponse response = null; try { // 配置信息 RequestConfig requestConfig = RequestConfig.custom() // 设置连接超时时间(单位毫秒) .setConnectTimeout(5000) // 设置请求超时时间(单位毫秒) .setConnectionRequestTimeout(5000) // socket读写超时时间(单位毫秒) .setSocketTimeout(5000) // 设置是否允许重定向(默认为true) .setRedirectsEnabled(true).build(); // 将上面的配置信息 运用到这个Get请求里 httpGet.setConfig(requestConfig); // 由客户端执行(发送)Get请求 response = httpClient.execute(httpGet); // 从响应模型中获取响应实体 HttpEntity responseEntity = response.getEntity(); System.out.println("响应状态为:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("响应内容长度为:" + responseEntity.getContentLength()); System.out.println("响应内容为:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 释放资源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
对应接收实例:
POST有参(普通参数):
注:POST传递普通参数时,方式与GET一样即可,这里以直接在url后缀上参数的方式示例。
HttpClient发送示例:
@Test public void doPostTestFour() { // 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 参数 StringBuffer params = new StringBuffer(); try { // 字符数据最好encoding以下;这样一来,某些特殊字符才能传过去(如:某人的名字就是“&”,不encoding的话,传不过去) params.append("name=" + URLEncoder.encode("&", "utf-8")); params.append("&"); params.append("age=24"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } // 创建Post请求 HttpPost httpPost = new HttpPost("http://localhost:12345/doPostControllerFour" + "?" + params); // 设置ContentType(注:如果只是传普通参数的话,ContentType不一定非要用application/json) httpPost.setHeader("Content-Type", "application/json;charset=utf8"); // 响应模型 CloseableHttpResponse response = null; try { // 由客户端执行(发送)Post请求 response = httpClient.execute(httpPost); // 从响应模型中获取响应实体 HttpEntity responseEntity = response.getEntity(); System.out.println("响应状态为:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("响应内容长度为:" + responseEntity.getContentLength()); System.out.println("响应内容为:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 释放资源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
对应接收示例:
Spring整合HttpClient
1.编辑pro配置文件
#最大连接数
http.maxTotal = 1000
#并发数
http.defaultMaxPerRoute = 20
#创建连接的最长时间
http.connectTimeout=5000
#从连接池中获取到连接的最长时间
http.connectionRequestTimeout=500
#数据传输的最长时间
http.socketTimeout=5000
#提交请求前测试连接是否可用
http.staleConnectionCheckEnabled=true
2.编辑配置类
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(); } }
3. 编辑关闭连接配置文件
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(); //全部从等待中唤醒.执行关闭操作; } } }
5. 编辑工具API
package com.jt.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @Service public class HttpClientService { @Autowired private CloseableHttpClient httpClient; @Autowired private RequestConfig requestConfig; /** * 编辑工具API目的简化代码,实现松耦合 * * 作用:帮助用户发起http请求,获取正确的结果返回给用户 * 参数设计: 1.用户url地址 2.Map<参数名,参数值> 3.字符编码 * * get请求方式: * 1.没有参数时: http://www.baidu.com * 2.有参数时: http://www.baidu.com?key1=value1&key2=value2... * @param url * @param params * @param charset * @return */ public String doGet(String url,Map<String,String> params,String charset) { //1.校验用户是否传递字符编码 if(StringUtils.isEmpty(charset)) { charset = "UTF-8"; } //2.封装URL地址 // http://www.baidu.com?key1=value1&key2=value2&... if(params != null) { url = url + "?"; //map遍历 for (Map.Entry<String,String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); url += key+"="+value+"&"; } //截去多余的& url = url.substring(0, url.length()-1); } //3.定义httpGet请求对象 HttpGet httpGet = new HttpGet(url); //设定请求的超时时间 httpGet.setConfig(requestConfig); //定义返回值数据 String result = null; try { CloseableHttpResponse response = httpClient.execute(httpGet); if(response.getStatusLine().getStatusCode() == 200) { //表示请求正确 HttpEntity entity = response.getEntity(); result = EntityUtils.toString(entity,charset); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } return result; } //重载方法 public String doGet(String url) { return doGet(url, null, null); } public String doGet(String url,Map<String,String> params) { return doGet(url, params, null); } //实现httpClient POST提交 public String doPost(String url,Map<String,String> params,String charset){ String result = null; //1.定义请求类型 HttpPost post = new HttpPost(url); post.setConfig(requestConfig); //定义超时时间 //2.判断字符集是否为null if(StringUtils.isEmpty(charset)){ charset = "UTF-8"; } //3.判断用户是否传递参数 if(params !=null){ //3.2准备List集合信息 List<NameValuePair> parameters = new ArrayList<>(); //3.3将数据封装到List集合中 for (Map.Entry<String,String> entry : params.entrySet()) { parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } //3.1模拟表单提交 try { UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,charset); //采用u8编码 //3.4将实体对象封装到请求对象中 post.setEntity(formEntity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //4.发送请求 try { CloseableHttpResponse response = httpClient.execute(post); //4.1判断返回值状态 if(response.getStatusLine().getStatusCode() == 200) { //4.2表示请求成功 result = EntityUtils.toString(response.getEntity(),charset); }else{ System.out.println("获取状态码信息:"+response.getStatusLine().getStatusCode()); throw new RuntimeException(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } public String doPost(String url){ return doPost(url, null, null); } public String doPost(String url,Map<String,String> params){ return doPost(url, params, null); } public String doPost(String url,String charset){ return doPost(url, null, charset); } }
6.编辑测试类调试程序
@Autowired private HttpClientService httpClient; @Test public void doGet() { //http://manage.jt.com/web/item/findItemById?itemId=1474391969 String url = "http://manage.jt.com/web/item/findItemById"; Map<String,String> params = new HashMap<>(); params.put("itemId", "562379"); String json = httpClient.doGet(url, params); System.out.println(json); }
HttpClient调用过程