您的位置:首页 > 理论基础 > 计算机网络

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

2016-08-22 14:10 453 查看


版权声明:本文为博主原创文章,未经博主允许不得转载。如需转载请声明:【转自 http://blog.csdn.net/xiaoxian8023

上一篇文章中,简单分享一下封装HttpClient工具类的思路及部分代码,本文将分享如何实现插件式配置HttpClient对象。

如果你看过我前面的几篇关于HttpClient的文章或者官网示例,应该都知道HttpClient对象在创建时,都可以设置各种参数,但是却没有简单的进行封装,比如对我来说比较重要的3个:代理、ssl(包含绕过证书验证和自定义证书验证)、超时。还需要自己写。所以这里我就简单封装了一下,顺便还封装了一个连接池的配置。

其实说是插件式配置,那是高大上的说法,说白了,就是采用了建造者模式来创建HttpClient对象(级联调用)。HttpClient的jar包中提供了一个创建HttpClient对象的类HttpClientBuilder。所以我是创建该类的子类HCB,然后做了一些改动。每个配置方法的返回值都是HCB,这样就支持级联调用了。具体代码如下:

[java] view
plain copy

 print?

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<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder  

                .<ConnectionSocketFactory> 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<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder  

                .<ConnectionSocketFactory> 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方法就可以了。于是乎就出现了这样简单、方便又明了的调用方式:

[java] view
plain copy

 print?

HttpClient client = HCB.custom().timeout(10000).proxy("127.0.0.1", 8087).ssl("D:\\keys\\wsriakey","tomcat").build();  

说到ssl,还需要另外一个封装的类,为了其他工具类有可能也会用到ssl,所以就单出来了。不多解释,直接上代码:

[java] view
plain copy

 print?

/** 

 * 设置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。方便调用。具体代码如下:

[java] view
plain copy

 print?

//默认采用的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时,就直接用下面的方式调用:

[java] view
plain copy

 print?

public static void testSimple() throws HttpProcessException{  

    String url = "http://tool.oschina.net/";  

    //简单调用  

    String resp = HttpClientUtil.send(url);  

    System.out.println("请求结果内容长度:"+ resp);  

}  

好了,插件化配置HttpClient,就是这些内容,在下一篇文章中分享如何插件式配置Header。没错,思路还是跟本文一样。敬请期待吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐