<!-- httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpasyncclient</artifactId><version>4.1</version></dependency><!-- gson --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.2</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
pom.xml 这里添加了gson依赖是为了后面发送JSON数据POST请求,添加lombook是为了简化实体对象(如不需要可不添加)
application.yml中添加http配置的一些信息
#http配置服务http:maxTotal: 100 #最大连接数defaultMaxPerRoute : 20 #并发数connectTimeout: 1000 #创建连接的最长时间connectionRequestTimeout: 500 #从连接池中获取到连接的最长时间socketTimeout: 10000 #数据传输的最长时间staleConnectionCheckEnabled: true #提交请求前测试连接是否可用
创建HttpClient实体类关联配置
@Configurationpublic class HttpClient {@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;/*** 首先实例化一个连接池管理器,设置最大连接数、并发连接数* @return*/@Bean(name = "httpClientConnectionManager")public PoolingHttpClientConnectionManager getHttpClientConnectionManager(){PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager();//最大连接数httpClientConnectionManager.setMaxTotal(maxTotal);//并发数httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);return httpClientConnectionManager;}/*** 实例化连接池,设置连接池管理器。* 这里需要以参数形式注入上面实例化的连接池管理器* @param httpClientConnectionManager* @return*/@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*/@Beanpublic CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){return httpClientBuilder.build();}/*** Builder是RequestConfig的一个内部类* 通过RequestConfig的custom方法来获取到一个Builder对象* 设置builder的连接信息* 这里还可以设置proxy,cookieSpec等属性。有需要的话可以在此设置* @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*/@Beanpublic RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){return builder.build();}}
创建连接线程处理类
@Componentpublic class IdleConnectionEvictor extends Thread {@Autowiredprivate HttpClientConnectionManager connMgr;private volatile boolean shutdown;public IdleConnectionEvictor() {super();super.start();}@Overridepublic void run() {try {while (!shutdown) {synchronized (this) {wait(5000);// 关闭失效的连接connMgr.closeExpiredConnections();}}} catch (InterruptedException ex) {// 结束}}//关闭清理无效连接的线程public void shutdown() {shutdown = true;synchronized (this) {notifyAll();}}}
创建请求返回结果类
@Datapublic class HttpResult {// 响应码@NonNullprivate Integer code;// 响应体@NonNullprivate String body;}
创建具体请求类
加入了请求Https、重定向时可能会遇到问题的通用解决方案
@Componentpublic class HttpAPIService {private static CloseableHttpClient httpClient;/*** 信任SSL证书*/static {try {SSLContext sslContext = SSLContextBuilder.create().useProtocol(SSLConnectionSocketFactory.SSL).loadTrustMaterial((x, y) -> true).build();RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).setSSLContext(sslContext).setSSLHostnameVerifier((x, y) -> true).build();} catch (Exception e) {e.printStackTrace();}}@Autowiredprivate RequestConfig config;/*** 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null** @param url* @return* @throws Exception*/public String doGet(String url) throws Exception {// 声明 http get 请求HttpGet httpGet = new HttpGet(url);// 装载配置信息httpGet.setConfig(config);// 允许重定向httpGet.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);// 发起请求CloseableHttpResponse response = this.httpClient.execute(httpGet);// 判断状态码是否为200if (response.getStatusLine().getStatusCode() == 200) {// 返回响应体的内容return EntityUtils.toString(response.getEntity(), "UTF-8");}return null;}/*** 带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null** @param url* @return* @throws Exception*/public String doGet(String url, Map<String, Object> map) throws Exception {URIBuilder uriBuilder = new URIBuilder(url);if (map != null) {// 遍历map,拼接请求参数for (Map.Entry<String, Object> entry : map.entrySet()) {uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());}}// 调用不带参数的get请求return this.doGet(uriBuilder.build().toString());}/*** @description 带参数的post请求* @param url* @param map* @param headers* @return com.mark.httpclient.HttpResult* @author Mario* @date 2019/7/15 14:39*/public HttpResult doPost(String url, Map<String, Object> map , Map<String,Object> headers) throws Exception {// 声明httpPost请求HttpPost httpPost = new HttpPost(url);// 加入配置信息httpPost.setConfig(config);// 判断map是否为空,不为空则进行遍历,封装from表单对象if (map != null) {List<NameValuePair> list = new ArrayList<NameValuePair>();for (Map.Entry<String, Object> entry : map.entrySet()) {list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));}// 构造from表单对象UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");// 把表单放到post里httpPost.setEntity(urlEncodedFormEntity);}// 设置请求头if (headers != null) {for (String key : headers.keySet()) {String value = headers.get(key).toString();httpPost.addHeader(key,value);}}// 发起请求CloseableHttpResponse response = this.httpClient.execute(httpPost);return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), "UTF-8"));}/*** 不带参数post请求** @param url* @return* @throws Exception*/public HttpResult doPost(String url) throws Exception {return this.doPost(url, null,null);}/*** @description 带参以JSON方式发送的post请求* @param url* @param jsonString* @param headers* @return com.mark.httpclient.HttpResult* @author Mario* @date 2019/7/15 14:39*/public static HttpResult doPostWithJson(String url, String jsonString, Map<String,Object> headers) throws Exception {// 声明httpPost请求HttpPost httpPost = new HttpPost(url);// 设置请求头if (headers != null) {for (String key : headers.keySet()) {String value = headers.get(key).toString();httpPost.addHeader(key, value);}}// 设置以Json数据方式发送StringEntity stringEntity = new StringEntity(jsonString, "utf-8");stringEntity.setContentType("application/json");httpPost.setEntity(stringEntity);// 发起请求CloseableHttpResponse response = httpClient.execute(httpPost);return new HttpResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), "UTF-8"));}}
