轻松把玩HttpClient之封装HttpClient工具类(二),插件式配置HttpClient对象

最后更新于:2022-04-01 22:59:55

     版权声明:本文为博主原创文章,未经博主允许不得转载。如需转载请声明:【转自 http://blog.csdn.net/xiaoxian8023 】      上一篇文章中,简单分享一下封装HttpClient工具类的思路及部分代码,本文将分享如何实现插件式配置HttpClient对象。      如果你看过我前面的几篇关于HttpClient的文章或者官网示例,应该都知道HttpClient对象在创建时,都可以设置各种参数,但是却没有简单的进行封装,比如对我来说比较重要的3个:代理、ssl(包含绕过证书验证和自定义证书验证)、超时。还需要自己写。所以这里我就简单封装了一下,顺便还封装了一个连接池的配置。      其实说是插件式配置,那是高大上的说法,说白了,就是采用了建造者模式来创建HttpClient对象(级联调用)。HttpClient的jar包中提供了一个创建HttpClient对象的类HttpClientBuilder。所以我是创建该类的子类HCB,然后做了一些改动。每个配置方法的返回值都是HCB,这样就支持级联调用了。具体代码如下: ~~~ package com.tgb.ccl.http.httpclient.builder; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import com.tgb.ccl.http.common.SSLs; import com.tgb.ccl.http.exception.HttpProcessException; /** * httpclient创建者 * * @author arron * @date 2015年11月9日 下午5:45:47 * @version 1.0 */ public class HCB extends HttpClientBuilder{ private boolean isSetPool=false;//记录是否设置了连接池 private boolean isNewSSL=false;//记录是否设置了更新了ssl //用于配置ssl private SSLs ssls = SSLs.getInstance(); private HCB(){} public static HCB custom(){ return new HCB(); } /** * 设置超时时间 * * @param timeout 超市时间,单位-毫秒 * @return */ public HCB timeout(int timeout){ // 配置请求的超时设置 RequestConfig config = RequestConfig.custom() .setConnectionRequestTimeout(timeout) .setConnectTimeout(timeout) .setSocketTimeout(timeout) .build(); return (HCB) this.setDefaultRequestConfig(config); } /** * 设置ssl安全链接 * * @return * @throws HttpProcessException */ public HCB ssl() throws HttpProcessException { if(isSetPool){//如果已经设置过线程池,那肯定也就是https链接了 if(isNewSSL){ throw new HttpProcessException("请先设置ssl,后设置pool"); } return this; } Registry socketFactoryRegistry = RegistryBuilder . create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", ssls.getSSLCONNSF()).build(); //设置连接池大小 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); return (HCB) this.setConnectionManager(connManager); } /** * 设置自定义sslcontext * * @param keyStorePath 密钥库路径 * @return * @throws HttpProcessException */ public HCB ssl(String keyStorePath) throws HttpProcessException{ return ssl(keyStorePath,"nopassword"); } /** * 设置自定义sslcontext * * @param keyStorePath 密钥库路径 * @param keyStorepass 密钥库密码 * @return * @throws HttpProcessException */ public HCB ssl(String keyStorePath, String keyStorepass) throws HttpProcessException{ this.ssls = SSLs.custom().customSSL(keyStorePath, keyStorepass); this.isNewSSL=true; return ssl(); } /** * 设置连接池(默认开启https) * * @param maxTotal 最大连接数 * @param defaultMaxPerRoute 每个路由默认连接数 * @return * @throws HttpProcessException */ public HCB pool(int maxTotal, int defaultMaxPerRoute) throws HttpProcessException{ Registry socketFactoryRegistry = RegistryBuilder . create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", ssls.getSSLCONNSF()).build(); //设置连接池大小 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); connManager.setMaxTotal(maxTotal); connManager.setDefaultMaxPerRoute(defaultMaxPerRoute); isSetPool=true; return (HCB) this.setConnectionManager(connManager); } /** * 设置代理 * * @param hostOrIP 代理host或者ip * @param port 代理端口 * @return */ public HCB proxy(String hostOrIP, int port){ // 依次是代理地址,代理端口号,协议类型 HttpHost proxy = new HttpHost(hostOrIP, port, "http"); DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); return (HCB) this.setRoutePlanner(routePlanner); } } ~~~      大家可以看到,这个有成员变量,而且不是static类型,所以是非线程安全的。所以我为了方便使用,就效仿HttpClients(其custom方法可以创建HttpClientBuilder实例)写了一个静态的custom方法,来返回一个新的HCB实例。将构造方法设置成了private,无法通过new的方式创建实例,所以只能通过custom方法来创建。在想生成HttpClient对象的时候,调用一下build方法就可以了。于是乎就出现了这样简单、方便又明了的调用方式: ~~~ HttpClient client = HCB.custom().timeout(10000).proxy("127.0.0.1", 8087).ssl("D:\\keys\\wsriakey","tomcat").build(); ~~~      说到ssl,还需要另外一个封装的类,为了其他工具类有可能也会用到ssl,所以就单出来了。不多解释,直接上代码: ~~~ /** * 设置ssl * * @author arron * @date 2015年11月3日 下午3:11:54 * @version 1.0 */ public class SSLs { private static final SSLHandler simpleVerifier = new SSLHandler(); private static SSLConnectionSocketFactory sslConnFactory ; private static SSLs sslutil = new SSLs(); private SSLContext sc; public static SSLs getInstance(){ return sslutil; } public static SSLs custom(){ return new SSLs(); } // 重写X509TrustManager类的三个方法,信任服务器证书 private static class SSLHandler implements X509TrustManager, HostnameVerifier{ @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } @Override public boolean verify(String paramString, SSLSession paramSSLSession) { return true; } }; // 信任主机 public static HostnameVerifier getVerifier() { return simpleVerifier; } public synchronized SSLConnectionSocketFactory getSSLCONNSF() throws HttpProcessException { if (sslConnFactory != null) return sslConnFactory; try { SSLContext sc = getSSLContext(); sc.init(null, new TrustManager[] { simpleVerifier }, null); sslConnFactory = new SSLConnectionSocketFactory(sc, simpleVerifier); } catch (KeyManagementException e) { throw new HttpProcessException(e); } return sslConnFactory; } public SSLs customSSL(String keyStorePath, String keyStorepass) throws HttpProcessException{ FileInputStream instream =null; KeyStore trustStore = null; try { trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); instream = new FileInputStream(new File(keyStorePath)); trustStore.load(instream, keyStorepass.toCharArray()); // 相信自己的CA和所有自签名的证书 sc= SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) .build(); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | KeyManagementException e) { throw new HttpProcessException(e); }finally{ try { instream.close(); } catch (IOException e) {} } return this; } public SSLContext getSSLContext() throws HttpProcessException{ try { if(sc==null){ sc = SSLContext.getInstance("SSLv3"); } return sc; } catch (NoSuchAlgorithmException e) { throw new HttpProcessException(e); } } } ~~~      基本上就是这样了。在上一篇中遗留了一个小问题,正好在这里说一下。上一篇文中说道提供一个默认的HttpClient实现,其实是2个,分别针对于http和https。方便调用。具体代码如下: ~~~ //默认采用的http协议的HttpClient对象 private static HttpClient client4HTTP; //默认采用的https协议的HttpClient对象 private static HttpClient client4HTTPS; static{ try { client4HTTP = HCB.custom().build(); client4HTTPS = HCB.custom().ssl().build(); } catch (HttpProcessException e) { logger.error("创建https协议的HttpClient对象出错:{}", e); } } /** * 判断url是http还是https,直接返回相应的默认client对象 * * @return 返回对应默认的client对象 * @throws HttpProcessException */ private static HttpClient create(String url) throws HttpProcessException { if(url.toLowerCase().startsWith("https://")){ return client4HTTPS; }else{ return client4HTTP; } } ~~~      这样在使用工具类的时候,如果不需要自定义HttpClient时,就直接用下面的方式调用: ~~~ public static void testSimple() throws HttpProcessException{ String url = "http://tool.oschina.net/"; //简单调用 String resp = HttpClientUtil.send(url); System.out.println("请求结果内容长度:"+ resp); } ~~~      好了,插件化配置HttpClient,就是这些内容,在下一篇文章中分享如何插件式配置Header。没错,思路还是跟本文一样。敬请期待吧。      代码已上传至:[https://github.com/Arronlong/httpclientUtil](https://github.com/Arronlong/httpclientUtil)。
';