HttpClilent整合Spring使用

1. 描述
HttpClient是appache组织开发的,感觉也比较全面,不管做爬虫还是特殊网络请求都还不错,如果没有基础的同学百度下吧 。说实话,httpclient用法确实挺简单的,不过当这些玩具代码要结合到实际开发中时还是要注意许多细节,为了加快写博客速度我就copy传智的玩具demo了,安静的夜晚码字好。。。安谧??

2. 依赖

 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><httpclient.version>4.3.5</httpclient.version></dependency

3. 从HTTP连接管理池中获取一个【连接】才是【最重要】的,Demo转成Spring配置方式

package cn.itcast.httpclient;import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;public class HttpConnectManager {public static void main(String[] args) throws Exception {PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();// 设置最大连接数cm.setMaxTotal(200);// 设置每个主机地址的并发数cm.setDefaultMaxPerRoute(20);doGet(cm);doGet(cm);}public static void doGet(HttpClientConnectionManager cm) throws Exception {CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();// 创建http GET请求HttpGet httpGet = new HttpGet("http://www.baidu.com/");CloseableHttpResponse response = null;try {// 执行请求response = httpClient.execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {String content = EntityUtils.toString(response.getEntity(), "UTF-8");System.out.println("内容长度:" + content.length());}} finally {if (response != null) {response.close();}// 此处不能关闭httpClient,如果关闭httpClient,连接池也会销毁// httpClient.close();}}}

扫一眼这个代码不就是获取连接发一个GET请求返回数据嘛,当然我们关注点需要集中在获取一个请求连接。我们要这样去理解,在Spring中以bean方式获取一个连接,这样是不是很方便,我们操作因为涉及到连接,对比数据源当然也能理解使用一个连接管理池 【PoolingHttpClientConnectionManager】,毕竟“池”的概念太通用了。从pool中借一个可关闭连接【CloseableHttpClient】有借有还工作起来效率才高。

  • 好了开始正题,创建HttpClient连接管理器
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();// 设置最大连接数cm.setMaxTotal(200);// 设置每个主机地址的并发数cm.setDefaultMaxPerRoute(20);
 <!-- 创建httpclient管理器 --><bean id="connectionManager"class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager"><!-- 最大连接数 --><property name="maxTotal" value="${http.maxTotal}" /><!-- 每个连接并发数 --><property name="defaultMaxPerRoute" value="${http.DefaultMaxPerRoute}" /></bean>

这个很好理解吧,创建了一个HTTP连接管理池,beanId=“connectionManager”,最大连接数和并发数在配置文件中配置:
http.maxTotal=200
http.DefaultMaxPerRoute=50

  • 获取HttpClientBuilder和CloseableHttpClient
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

看看源码 HttpClients.custom()的实现:

    /*** Creates builder object for construction of custom* {@link CloseableHttpClient} instances.*/public static HttpClientBuilder custom() {return HttpClientBuilder.create();}=========================================================public static HttpClientBuilder create() {return new HttpClientBuilder();}

无非就是 new HttpClientBuilder();ok那么我们可以用配置来解决:

    <!-- HttpClient构建器 --><bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder"><!-- 注入http_client管理器 --><property name="ConnectionManager" ref="connectionManager" /></bean>

至于property name="ConnectionManager"这个配置,不就是HttpClientBuilder.setConnectionManager(cm);最后一个HttpClientBuilder.build()对应源码:

public CloseableHttpClient build(){//该处实现代码省略。。。// 返回了我们所需的CloseableHttpClient // 不过需要注意下:该方法不是静态方法,为什么呢?因为我们所需的HttpClient连接是一种无状态请求// 一次请求一次打开和关闭,需要创建新的连接,所以不能用static修饰}

对应配置文件:

    <!--HttpCient对象 --><bean id="httpclient" class="org.apache.http.impl.client.CloseableHttpClient"factory-bean="httpClientBuilder" factory-method="build" scope="prototype"></bean>

factory-method="build"这个标签用HttpClientBuilder的工厂方法build创建CloseableHttpClient ,还有值得注意的是:scope=“prototype”,一定不是sington

4. 设置CloseableHttpClient的请求配置信息
前面3个步骤拿到了CloseableHttpClient,但还得为HttpClient对象设置请求配置信息,其中无非就是从pool中获取超时设置,请求本身的某些生命周期配置,Demo如下:

public class RequestConfigDemo {public static void main(String[] args) throws Exception {// 创建Httpclient对象CloseableHttpClient httpclient = HttpClients.createDefault();// 创建http GET请求HttpGet httpGet = new HttpGet("http://www.baidu.com/");// 构建请求配置信息RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 创建连接的最长时间.setConnectionRequestTimeout(500) // 从连接池中获取到连接的最长时间.setSocketTimeout(10 * 1000) // 数据传输的最长时间.setStaleConnectionCheckEnabled(true) // 提交请求前测试连接是否可用.build();// 设置请求配置信息httpGet.setConfig(config);CloseableHttpResponse response = null;try {// 执行请求response = httpclient.execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {String content = EntityUtils.toString(response.getEntity(), "UTF-8");System.out.println(content);}} finally {if (response != null) {response.close();}httpclient.close();}}}

========================================================================
// 构建请求配置信息
RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 创建连接的最长时间
.setConnectionRequestTimeout(500) // 从连接池中获取到连接的最长时间
.setSocketTimeout(10 * 1000) // 数据传输的最长时间
.setStaleConnectionCheckEnabled(true) // 提交请求前测试连接是否可用
.build();
// 设置请求配置信息
httpGet.setConfig(config); =========================================================================
这段就是核心了,RequestConfig.custom()对应源码:

    public static RequestConfig.Builder custom() {return new Builder();}

这个配置就很简单了,构建Builder的Spring配置如下:

<!-- 构建请求配置信息Builder --><bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder"><!-- 创建连接的最长时间 --><property name="connectTimeout" value="${http.connectTimeout}" /><!-- 从连接池中获取到连接的最长时间 --><property name="connectionRequestTimeout" value="${http.connectionRequestTimeout}" /><!-- 数据传输的最长时间 --><property name="socketTimeout" value="${http.socketTimeout}" /><!-- 提交请求前测试连接是否可用 --><property name="staleConnectionCheckEnabled" value="${http.staleConnectionCheckEnabled}" /></bean>

对应properties配置如下:
http.connectTimeout=1000
http.connectionRequestTimeout=500
http.socketTimeout=10000
http.staleConnectionCheckEnabled=true
对应Builder.build();源码如下:

  public RequestConfig build() {return new RequestConfig(expectContinueEnabled,proxy,localAddress,staleConnectionCheckEnabled,cookieSpec,redirectsEnabled,relativeRedirectsAllowed,circularRedirectsAllowed,maxRedirects,authenticationEnabled,targetPreferredAuthSchemes,proxyPreferredAuthSchemes,connectionRequestTimeout,connectTimeout,socketTimeout);}

好了该拿到RequestConfig 对象了,其实跟CloseableHttpClient对象一样都是通过工厂方法去获取:

 <bean id="requestConfig" class="org.apache.http.client.config.RequestConfig"factory-bean="requestConfigBuilder" factory-method="build"></bean>

这里不需要像CloseableHttpClient那样scope=“prototype”,配置属性一份就够了
至此我们有了CloseableHttpClient和RequestConfig 的配置,在开发中可以随意获取了。。。。

5. 编写HttpClient工具类

  • 1.最基本的Get请求:
package com.taotao.common.service;  import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
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.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.taotao.common.httpclient.HttpResult;@Service
public class ApiService implements BeanFactoryAware{@Autowired(required=false)private RequestConfig requestConfig;private BeanFactory beanFactory;/*** 200返回数据,其它为null* @author: kaixing  * @createTime: 2018年9月13日 下午10:40:00  * @history:  * @param url* @return* @throws ClientProtocolException* @throws IOException String*/public String doGet(String url) throws ClientProtocolException, IOException{// 创建http GET请求HttpGet httpGet = new HttpGet(url);httpGet.setConfig(this.requestConfig);CloseableHttpResponse response = null;try {// 执行请求response = createHttpClient().execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {return EntityUtils.toString(response.getEntity(), "UTF-8");}} finally {if (response != null) {response.close();}}return null;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}private CloseableHttpClient createHttpClient(){return this.beanFactory.getBean(CloseableHttpClient.class);}}

可能最不能理解的就是实现BeanFactoryAware接口了,解释如下:
1.很明显ApiService 作为一个工具服务类,在Spring容器中只有一个实例,@Service注解也说明如此。我们很有可能会想到将CloseableHttpClient以注入方式注入进来,eg:
@Autowired
private CloseableHttpClient httpclient;
因为我们前面不是配置了CloseableHttpClient 这个对象吗,所以这样做有问题吗?
2.如果我们以注入的方式去获取CloseableHttpClient ,想想我们是不是永远只能获取到同一个CloseableHttpClient 对象,为什么?我们不是将CloseableHttpClient 设置成多实例的吗?。。。因为在一个service中注入另一个对象,在初始化时就已经确定了,这个对象怎么可能不同!!!!
3.怎么解决呢?所以实现BeanFactoryAware这个接口就有原因了,看源码:

 * @author Rod Johnson* @author Chris Beams* @since 11.03.2003* @see BeanNameAware* @see BeanClassLoaderAware* @see InitializingBean* @see org.springframework.context.ApplicationContextAware*/
public interface BeanFactoryAware extends Aware {/*** Callback that supplies the owning factory to a bean instance.* <p>Invoked after the population of normal bean properties* but before an initialization callback such as* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.* @param beanFactory owning BeanFactory (never {@code null}).* The bean can immediately call methods on the factory.* @throws BeansException in case of initialization errors* @see BeanInitializationException*/void setBeanFactory(BeanFactory beanFactory) throws BeansException;}

这个接口给我们提供了BeanFactory 这个对象,玛德这个对象实在太重要了!!!!

  private BeanFactory beanFactory;......@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;//获取到spring提供的该对象保存起来}private CloseableHttpClient createHttpClient(){return this.beanFactory.getBean(CloseableHttpClient.class);// 该处是不是完美解决拿不到多个实例的问题}

所以总结下就是在单例Service中想要获取多实例的注入属性,注入方式肯定不行的,实现BeanFactory 类似接口是ok的。

6. 结语
想要完整代码的可以参考 HttpClilent整合Spring使用【配置和代码】

HttpClilent整合Spring使用相关推荐

  1. spring boot整合spring security笔记

    最近自己做了一个小项目,正在进行springboot和spring Security的整合,有一丢丢的感悟,在这里分享一下: 首先,spring boot整合spring security最好是使用T ...

  2. Echache整合Spring缓存实例讲解

    2019独角兽企业重金招聘Python工程师标准>>> 摘要:本文主要介绍了EhCache,并通过整合Spring给出了一个使用实例. 一.EhCache 介绍 EhCache 是一 ...

  3. 八、springboot整合Spring Security

    springboot整合Spring Security 简介 Spring Security是一个功能强大且可高度自定义的身份验证和访问控制框架.它是保护基于Spring的应用程序的事实标准. Spr ...

  4. Activiti工作流从入门到入土:整合spring

    文章源码托管:https://github.com/OUYANGSIHAI/Activiti-learninig 欢迎 star !!! 一.前言 在上一节中,通过一个入门程序,把activiti的环 ...

  5. springboot2 war页面放在那_Spring Boot2 系列教程(三十三)整合 Spring Security

    Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经发展了多年了,但是使用的并不多,安全管理 ...

  6. springboot templates读取不到_整合spring mvc + mybatis,其实很简单,spring boot实践(5)

    01 spring boot读取配置信息 02 多环境配置 03 处理全局异常 04 spring boot admin 主要通过spring boot整合spring mvc 以及mybatis实现 ...

  7. springboot整合hibernate_峰哥说技术系列-17 .Spring Boot 整合 Spring Data JPA

    今日份主题 Spring Boot 整合 Spring Data JPA JPA(Java Persistence API)是用于对象持久化的 API,是Java EE 5.0 平台标准的 ORM 规 ...

  8. ActiveMQ整合spring

    主要讲解点: 使用ActiveMQ完成发短信功能(重点) 2.Kindeditor上传图片及图片管理器功能的实现(对应后台代码是重点, Kindeditor参照demo会用就行) 宣传活动的保存功能( ...

  9. CXF WebService整合Spring

    CXF WebService整合Spring 首先,CXF和spring整合需要准备如下jar包文件: 这边我是用Spring的jar包是Spring官方提供的,并没有使用CXF中的Spring的ja ...

  10. cxf整合spring错误为:cvc-complex-type.2.4.c

    cxf整合spring,报错信息如下: Multiple annotations found at this line: - cvc-complex-type.2.4.c: The matching ...

最新文章

  1. 中国电子信息工程科技发展十四大趋势(2021)
  2. hdu 2709 递推
  3. 手持终端可以根据行业的功能要求进行定制
  4. 和我一起来分析某药品仓储管理系统 卡死现象
  5. 2020牛客国庆集训派对day1 Zeldain Garden
  6. 程序包java.awt不存在_IDEA解决Java:程序包xxxx不存在的问题
  7. mysql 命令备忘
  8. regester正则用法_Regester(正则表达式测试器)
  9. 谷歌地图高精度模型提取1
  10. 刽子手游戏 C语言实现
  11. Ubuntu18.04配置运行Kintinuous
  12. 【U8+】修改或删除凭证提示此分录两清
  13. Java自定义变换产生摘要数据
  14. 工业三防平板可应用于各种复杂苛刻的工作环境
  15. mysql 必知必会【沈剑——公众号架构师之路】
  16. excel python 文字中间横杠_怎么在“excel”中的文字中间划一道横线?
  17. 二叉树构建的各种方法
  18. RNA与DNA曾是一体?生命起源论或被颠覆
  19. ESP8266 固件下载
  20. python的mag模块_Python Decimal max_mag()用法及代码示例

热门文章

  1. simpleDateFormat 和 TimeZone
  2. JSP入门之表格以及常用表单元素(总结自身编程经验以及多本教科书)
  3. 网页进行QQ聊天简单应用
  4. 6个免抠素材网站,免费可商用
  5. python使用ip地址定位_python实现ip地址查询经纬度定位详解
  6. lbj学习日记02 循环结构(for while)
  7. 阿里云商标注册流程步骤-阿里云商标自助注册申请步骤...
  8. android直播sdk+美颜,短视频SDK,美颜SDK,直播SDK_提供开放API接口
  9. Bar Chart Race」动态可视化
  10. MPB:遗传发育所白洋组-​高通量分离培养和鉴定植物根系细菌