1. <!-- httpclient -->
  2. <dependency>
  3. <groupId>org.apache.httpcomponents</groupId>
  4. <artifactId>httpasyncclient</artifactId>
  5. <version>4.1</version>
  6. </dependency>
  7. <!-- gson -->
  8. <dependency>
  9. <groupId>com.google.code.gson</groupId>
  10. <artifactId>gson</artifactId>
  11. <version>2.8.2</version>
  12. </dependency>
  13. <!-- lombok -->
  14. <dependency>
  15. <groupId>org.projectlombok</groupId>
  16. <artifactId>lombok</artifactId>
  17. <optional>true</optional>
  18. </dependency>

pom.xml 这里添加了gson依赖是为了后面发送JSON数据POST请求,添加lombook是为了简化实体对象(如不需要可不添加)

application.yml中添加http配置的一些信息

  1. #http配置服务
  2. http:
  3. maxTotal: 100 #最大连接数
  4. defaultMaxPerRoute : 20 #并发数
  5. connectTimeout: 1000 #创建连接的最长时间
  6. connectionRequestTimeout: 500 #从连接池中获取到连接的最长时间
  7. socketTimeout: 10000 #数据传输的最长时间
  8. staleConnectionCheckEnabled: true #提交请求前测试连接是否可用

创建HttpClient实体类关联配置

  1. @Configuration
  2. public class HttpClient {
  3. @Value("${http.maxTotal}")
  4. private Integer maxTotal;
  5. @Value("${http.defaultMaxPerRoute}")
  6. private Integer defaultMaxPerRoute;
  7. @Value("${http.connectTimeout}")
  8. private Integer connectTimeout;
  9. @Value("${http.connectionRequestTimeout}")
  10. private Integer connectionRequestTimeout;
  11. @Value("${http.socketTimeout}")
  12. private Integer socketTimeout;
  13. @Value("${http.staleConnectionCheckEnabled}")
  14. private boolean staleConnectionCheckEnabled;
  15. /**
  16. * 首先实例化一个连接池管理器,设置最大连接数、并发连接数
  17. * @return
  18. */
  19. @Bean(name = "httpClientConnectionManager")
  20. public PoolingHttpClientConnectionManager getHttpClientConnectionManager(){
  21. PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager();
  22. //最大连接数
  23. httpClientConnectionManager.setMaxTotal(maxTotal);
  24. //并发数
  25. httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
  26. return httpClientConnectionManager;
  27. }
  28. /**
  29. * 实例化连接池,设置连接池管理器。
  30. * 这里需要以参数形式注入上面实例化的连接池管理器
  31. * @param httpClientConnectionManager
  32. * @return
  33. */
  34. @Bean(name = "httpClientBuilder")
  35. public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){
  36. //HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
  37. HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
  38. httpClientBuilder.setConnectionManager(httpClientConnectionManager);
  39. return httpClientBuilder;
  40. }
  41. /**
  42. * 注入连接池,用于获取httpClient
  43. * @param httpClientBuilder
  44. * @return
  45. */
  46. @Bean
  47. public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){
  48. return httpClientBuilder.build();
  49. }
  50. /**
  51. * Builder是RequestConfig的一个内部类
  52. * 通过RequestConfig的custom方法来获取到一个Builder对象
  53. * 设置builder的连接信息
  54. * 这里还可以设置proxy,cookieSpec等属性。有需要的话可以在此设置
  55. * @return
  56. */
  57. @Bean(name = "builder")
  58. public RequestConfig.Builder getBuilder(){
  59. RequestConfig.Builder builder = RequestConfig.custom();
  60. return builder.setConnectTimeout(connectTimeout)
  61. .setConnectionRequestTimeout(connectionRequestTimeout)
  62. .setSocketTimeout(socketTimeout)
  63. .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
  64. }
  65. /**
  66. * 使用builder构建一个RequestConfig对象
  67. * @param builder
  68. * @return
  69. */
  70. @Bean
  71. public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){
  72. return builder.build();
  73. }
  74. }

创建连接线程处理类

  1. @Component
  2. public class IdleConnectionEvictor extends Thread {
  3. @Autowired
  4. private HttpClientConnectionManager connMgr;
  5. private volatile boolean shutdown;
  6. public IdleConnectionEvictor() {
  7. super();
  8. super.start();
  9. }
  10. @Override
  11. public void run() {
  12. try {
  13. while (!shutdown) {
  14. synchronized (this) {
  15. wait(5000);
  16. // 关闭失效的连接
  17. connMgr.closeExpiredConnections();
  18. }
  19. }
  20. } catch (InterruptedException ex) {
  21. // 结束
  22. }
  23. }
  24. //关闭清理无效连接的线程
  25. public void shutdown() {
  26. shutdown = true;
  27. synchronized (this) {
  28. notifyAll();
  29. }
  30. }
  31. }

创建请求返回结果类

  1. @Data
  2. public class HttpResult {
  3. // 响应码
  4. @NonNull
  5. private Integer code;
  6. // 响应体
  7. @NonNull
  8. private String body;
  9. }

创建具体请求类

加入了请求Https、重定向时可能会遇到问题的通用解决方案

  1. @Component
  2. public class HttpAPIService {
  3. private static CloseableHttpClient httpClient;
  4. /**
  5. * 信任SSL证书
  6. */
  7. static {
  8. try {
  9. SSLContext sslContext = SSLContextBuilder.create().useProtocol(SSLConnectionSocketFactory.SSL)
  10. .loadTrustMaterial((x, y) -> true).build();
  11. RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build();
  12. httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).setSSLContext(sslContext)
  13. .setSSLHostnameVerifier((x, y) -> true).build();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. @Autowired
  19. private RequestConfig config;
  20. /**
  21. * 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
  22. *
  23. * @param url
  24. * @return
  25. * @throws Exception
  26. */
  27. public String doGet(String url) throws Exception {
  28. // 声明 http get 请求
  29. HttpGet httpGet = new HttpGet(url);
  30. // 装载配置信息
  31. httpGet.setConfig(config);
  32. // 允许重定向
  33. httpGet.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);
  34. // 发起请求
  35. CloseableHttpResponse response = this.httpClient.execute(httpGet);
  36. // 判断状态码是否为200
  37. if (response.getStatusLine().getStatusCode() == 200) {
  38. // 返回响应体的内容
  39. return EntityUtils.toString(response.getEntity(), "UTF-8");
  40. }
  41. return null;
  42. }
  43. /**
  44. * 带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
  45. *
  46. * @param url
  47. * @return
  48. * @throws Exception
  49. */
  50. public String doGet(String url, Map<String, Object> map) throws Exception {
  51. URIBuilder uriBuilder = new URIBuilder(url);
  52. if (map != null) {
  53. // 遍历map,拼接请求参数
  54. for (Map.Entry<String, Object> entry : map.entrySet()) {
  55. uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
  56. }
  57. }
  58. // 调用不带参数的get请求
  59. return this.doGet(uriBuilder.build().toString());
  60. }
  61. /**
  62. * @description 带参数的post请求
  63. * @param url
  64. * @param map
  65. * @param headers
  66. * @return com.mark.httpclient.HttpResult
  67. * @author Mario
  68. * @date 2019/7/15 14:39
  69. */
  70. public HttpResult doPost(String url, Map<String, Object> map , Map<String,Object> headers) throws Exception {
  71. // 声明httpPost请求
  72. HttpPost httpPost = new HttpPost(url);
  73. // 加入配置信息
  74. httpPost.setConfig(config);
  75. // 判断map是否为空,不为空则进行遍历,封装from表单对象
  76. if (map != null) {
  77. List<NameValuePair> list = new ArrayList<NameValuePair>();
  78. for (Map.Entry<String, Object> entry : map.entrySet()) {
  79. list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
  80. }
  81. // 构造from表单对象
  82. UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");
  83. // 把表单放到post里
  84. httpPost.setEntity(urlEncodedFormEntity);
  85. }
  86. // 设置请求头
  87. if (headers != null) {
  88. for (String key : headers.keySet()) {
  89. String value = headers.get(key).toString();
  90. httpPost.addHeader(key,value);
  91. }
  92. }
  93. // 发起请求
  94. CloseableHttpResponse response = this.httpClient.execute(httpPost);
  95. return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(
  96. response.getEntity(), "UTF-8"));
  97. }
  98. /**
  99. * 不带参数post请求
  100. *
  101. * @param url
  102. * @return
  103. * @throws Exception
  104. */
  105. public HttpResult doPost(String url) throws Exception {
  106. return this.doPost(url, null,null);
  107. }
  108. /**
  109. * @description 带参以JSON方式发送的post请求
  110. * @param url
  111. * @param jsonString
  112. * @param headers
  113. * @return com.mark.httpclient.HttpResult
  114. * @author Mario
  115. * @date 2019/7/15 14:39
  116. */
  117. public static HttpResult doPostWithJson(String url, String jsonString, Map<String,Object> headers) throws Exception {
  118. // 声明httpPost请求
  119. HttpPost httpPost = new HttpPost(url);
  120. // 设置请求头
  121. if (headers != null) {
  122. for (String key : headers.keySet()) {
  123. String value = headers.get(key).toString();
  124. httpPost.addHeader(key, value);
  125. }
  126. }
  127. // 设置以Json数据方式发送
  128. StringEntity stringEntity = new StringEntity(jsonString, "utf-8");
  129. stringEntity.setContentType("application/json");
  130. httpPost.setEntity(stringEntity);
  131. // 发起请求
  132. CloseableHttpResponse response = httpClient.execute(httpPost);
  133. return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(
  134. response.getEntity(), "UTF-8"));
  135. }
  136. }