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

本文介绍了一种插件式的配置HttpClient的方法,通过建造者模式实现了超时、代理、SSL等功能的配置,并提供了连接池的设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



上一篇文章中,简单分享一下封装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<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方法就可以了。于是乎就出现了这样简单、方便又明了的调用方式:

    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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值