`
dsxwjhf
  • 浏览: 73678 次
  • 性别: Icon_minigender_1
  • 来自: 安徽
社区版块
存档分类
最新评论

换一种口味实现 HttpClient

阅读更多
基于注解 + 反射 + 动态代理

先上代码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokerMethod {

    enum HttpMethod {
        Get, Post
    }

    HttpMethod method() default HttpMethod.Get;

    String path() default "";

    int timeout() default 5000;

}

public class HttpProxyFactoryBean implements FactoryBean {
    private String interfaceName;
    private InvocationHandler handler;
    private Object proxy;
    private Class<?> proxyType;

    public void init() throws Exception {
        Preconditions.checkNotNull(interfaceName);
        Preconditions.checkNotNull(handler);

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        proxyType = ClassUtils.getClass(classLoader, interfaceName.trim());
        proxy = Proxy.newProxyInstance(classLoader, new Class[] { proxyType }, handler);
    }

    @Override
    public Object getObject() throws Exception {
        return proxy;
    }

    @Override
    public Class getObjectType() {
        return proxyType;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public void setHandler(InvocationHandler handler) {
        this.handler = handler;
    }

}

public class HttpInvocationHandler implements InvocationHandler {

    // 目标地址,如: http://www.example.com
    private String host = "******";
    // 申请的 key
    private String key = "******";
    // HttpClient
    private CloseableHttpClient httpClient;

    /**
     * 初始化 HttpClient 。 HttpClient 的构造其实很有讲究的。
     */
    public HttpInvocationHandler() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        // 设置总的最大连接数
        connectionManager.setMaxTotal(500);
        // 设置单机最大连接数
        connectionManager.setDefaultMaxPerRoute(100);
        // 设置出口到目标地址的单机最大连接数
        HttpHost httpHost = new HttpHost(parseHost()[1], 80);
        connectionManager.setMaxPerRoute(new HttpRoute(httpHost), 100);

        httpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .build();
    }

    /**
     * 代理方法,执行 http 请求。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Preconditions.checkNotNull(httpClient);
        Preconditions.checkNotNull(host);
        Preconditions.checkNotNull(key);

        HttpUriRequest httpRequest = buildHttpRequest(method, args);
        if (httpRequest == null) {
            throw new IllegalRequestException();
        }

        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpRequest);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new RemoteServiceException("Http status code: " + statusCode);
            }
            HttpEntity entity = httpResponse.getEntity();
            Object response = null;
            if (entity != null) {
                InputStream inputStream = entity.getContent();
                try {
                    response = JsonUtil.fromJson(new InputStreamReader(inputStream), method.getReturnType());
                } finally {
                    inputStream.close();
                }
            }
            return response;
        } catch (Exception e) {
            throw new RemoteServiceException(e);
        } finally {
            if (httpResponse != null) {
                httpResponse.close();
                httpRequest.abort();
            }
        }
    }

    /**
     * 构造 Http 请求。
     */
    private HttpUriRequest buildHttpRequest(Method method, Object[] args) {
        InvokerMethod invokerMethod = method.getAnnotation(InvokerMethod.class);
        if (invokerMethod == null) {
            return null;
        }
        if (args == null || args.length == 0) {
            return null;
        }
        Object request = args[0];
        String jsonRequest = JsonUtil.toJson(request);
        HttpUriRequest httpUriRequest;
        switch (invokerMethod.method()) {
        case Get:
            httpUriRequest = createGetRequest(invokerMethod, jsonRequest);
            break;
        case Post:
            httpUriRequest = createPostRequest(invokerMethod, jsonRequest);
            break;
        default:
            httpUriRequest = null;
            break;
        }
        return httpUriRequest;
    }

    /**
     * 创建加密 Get 请求。
     */
    private HttpUriRequest createGetRequest(InvokerMethod method, String jsonRequest) {
        URI uri;
        try {
            String[] hostPair = parseHost();
            uri = new URIBuilder()
                    .setScheme(hostPair[0])
                    .setHost(hostPair[1])
                    .setPath(method.path())
                    .addParameter("json", jsonRequest)
                    .addParameter("sign", encrypt(jsonRequest))
                    .addParameter("sign_type", "md5")
                    .build();
        } catch (URISyntaxException e) {
            return null;
        }
        RequestConfig config = RequestConfig.custom().setSocketTimeout(method.timeout()).build();
        HttpGet httpGet = new HttpGet(uri);
        httpGet.setConfig(config);
        return httpGet;
    }

    /**
     * 创建加密 Post 请求。
     */
    private HttpUriRequest createPostRequest(InvokerMethod method, String jsonRequest) {
        URI uri;
        try {
            String[] hostPair = parseHost();
            uri = new URIBuilder()
                    .setScheme(hostPair[0])
                    .setHost(hostPair[1])
                    .setPath(method.path())
                    .build();
        } catch (URISyntaxException e) {
            return null;
        }
        RequestConfig config = RequestConfig.custom().setSocketTimeout(method.timeout()).build();
        HttpPost httpPost = new HttpPost(uri);
        httpPost.setConfig(config);
        List<NameValuePair> pairs = Lists.newArrayListWithCapacity(3);
        pairs.add(new BasicNameValuePair("json", jsonRequest));
        pairs.add(new BasicNameValuePair("sign", encrypt(jsonRequest)));
        pairs.add(new BasicNameValuePair("sign_type", "md5"));
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, Consts.UTF_8));
        return httpPost;
    }

    /**
     * 使用 MD5 加密请求数据。
     */
    private String encrypt(String jsonRequest) {
        return DigestUtils.md5Hex(jsonRequest + key);
    }

    /**
     * http://www.example.com ==> [http, www.example.com] 。
     */
    private String[] parseHost() {
        if (host == null) {
            return new String[] { "", "" };
        }
        String[] parts = StringUtils.split(host, "://");
        if (parts.length != 2) {
            return new String[] { "", "" };
        }
        return parts;
    }

}




程序说明

1. InvokerMethod
该类比较简单,一个注解,它将作用于方法上,保留到运行期(这样才能通过反射获取其内容)。

2. HttpProxyFactoryBean
这个类比较奇特,也是这个解决方案的精华。
它实现了 FactoryBean 。 FactoryBean 是 Spring 类库的一个接口,它提供了三个方法需要实现:
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();

和普通 Bean 不同,该类被配置为 Spring Bean 后,返回的不是 FactoryBean 本身,而是它的 getObject() 所返回的对象。 getObjectType() 将返回实例的类型,isSingleton() 可选择是否使用单例模式。

具体到本类,在 init 方法中,初始化了动态代理类 proxy ,这个 proxy 将作为 getObject() 的返回。 interfaceName 和 handler 将作为属性在 Spring 配置文件中注入:
<bean id="receiptQueryService" class="com.******.HttpProxyFactoryBean" init-method="init">
    <property name="interfaceName" value="com.******.ReceiptQueryService"/>
    <property name="handler" ref="httpInvocationHandler"/>
</bean>


ReceiptQueryService 大概长这个样子:
public interface ReceiptQueryService {

    @InvokerMethod(method = InvokerMethod.HttpMethod.Get, path = "/xx/yy/zz")
    ReceiptQueryResponse queryReceipts(ReceiptQueryRequest request);

}

现在,当我们调用 http 服务的时候,只需要写一个接口,在方法上加一个注解就可以了,加密等操作对程序员完全透明!

3. HttpInvocationHandler
我们在第二步中用到了一个 InvocationHandler 。我知道,它是 java.lang.reflect.Proxy 构造动态代理类的第三个参数:
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

它只有一个必须实现的接口:
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;


在我们 InvocationHandler 的实现类里,将通过反射获取方法的注解( path | get/post | timeout )和参数:
InvokerMethod invokerMethod = method.getAnnotation(InvokerMethod.class);
...
invokerMethod.method();
invokerMethod.path();
invokerMethod.timeout();
...

Object request = args[0];
分享到:
评论

相关推荐

    httpclient所有jar包以及实现类

    在Java开发中,HTTPClient是一个常用的库,用于执行HTTP请求并处理响应。...本资源包含了一个名为"HTTPclient.jar"的文件,这正是Apache ...对于任何涉及网络通信的Java项目,HttpClient都是一个必不可少的工具。

    php实现httpclient类.zip

    主要介绍了php实现httpclient类示例,需要的朋友可以参考下,buffer 获取返回的字符串,referer 设置 HTTP_REFERER 的网址,response 服务器响应的 header 信息,request 发送到服务器的 header 信息。

    php实现httpclient类

    hp实现httpclient类示例,需要的朋友可以参考下,buffer 获取返回的字符串,referer 设置 HTTP_REFERER 的网址,response 服务器响应的 header 信息,request 发送到服务器的 header 信息。

    java实现httpClient样例

    1. **HttpClient的创建**:首先,你需要创建一个HttpClient实例。通常,你会使用`HttpClientBuilder`或`HttpClients`类来构建一个客户端对象。例如: ```java CloseableHttpClient httpClient = HttpClients....

    java实现HttpClient异步请求资源的方法

    本篇文章将详细介绍如何使用Java的HttpClient实现异步请求资源。 首先,让我们了解什么是异步请求。在同步请求中,调用一个API或发送一个HTTP请求后,程序会等待响应返回,然后继续执行后续代码。而异步请求则不同...

    HttpClient实现远程下载

    通过HttpClient实现远程下载,本例子通过java代码实现

    c#实现HttpClient拼接multipart/form-data形式参数post提交数据

    使用c#实现的HttpClient拼接multipart/form-data形式参数post提交数据,包含图片内容,有需要的可以下载,希望能帮到有需要的人,

    HttpClient实现文件下载

    ### HttpClient 实现文件下载 #### 一、简介与原理 在Java开发中,经常会遇到需要通过HTTP协议来获取网络资源的需求,例如从Web服务器下载文件。Apache HttpClient 是一个用于发送HTTP请求的Java类库,它提供了...

    HttpClient4.5 实现https忽略SSL证书验证

    使用HttpClient4.5实现https请求忽略SSL证书验证工具类

    httpclient实现webservice客户端

    在IT行业中,HttpClient是Apache软件基金会的一个开源项目,它提供了一种强大的、高度可定制的HTTP客户端API,用于处理HTTP协议。HttpClient库广泛应用于构建Java应用程序,尤其是那些需要与Web服务进行交互的程序。...

    基于QT的httpclient程序

    QT框架是一种广泛应用于桌面、移动和嵌入式平台的跨平台C++开发工具包,它提供了丰富的图形用户界面(GUI)功能以及网络编程接口。在本项目“基于QT的httpclient程序”中,我们将深入探讨如何使用QT来实现一个简单的...

    springmvc实现httpclient调用服务

    综上所述,Spring MVC结合HttpClient提供了一种有效的方式,使我们在Java Web应用中能够轻松地调用外部服务。通过合理的配置和编程,我们可以构建稳定、高效的远程服务调用系统。在实际开发中,还应根据具体需求进行...

    HTTPClient 的一个封装

    这个“HTTPClient的一个封装”显然指的是对Apache HttpClient库进行了定制化处理,以适应特定项目需求或者简化API使用。下面将详细讨论HttpClient的核心概念、封装的目的以及可能实现的方式。 HttpClient是Apache...

    HttpClientHelper 工具类

    单例模式是软件设计模式的一种,确保一个类只有一个实例,并提供一个全局访问点。HttpClientHelper 中的 SingleHelper 就是实现了单例模式的辅助类,为HttpClientHelper提供全局唯一的实例。这样做的好处是避免了...

    HttpClient实现POST GET和文件下载

    HttpClient提供了一种便捷的方式来下载文件: 1. **创建HttpGet请求**:指定要下载文件的URL。 2. **执行请求**:发送请求并获取`HttpResponse`。 3. **准备输出流**:创建一个本地文件,并用`FileOutputStream`...

    Httpclient+testng接口测试小例子

    2. **创建HttpClient实例**:在测试代码中,我们需要创建一个HttpClient实例。这可以通过`HttpClientBuilder`类来实现,可以定制化连接管理、超时设置等选项。 3. **构建HTTP请求**:使用HttpClient提供的方法,如`...

    httpclient post方式发送请求

    以下是一个示例代码,展示如何实现这一过程: ```java import org.apache.http.HttpEntity; import org.apache.http.client.entity.EntityBuilder; import org.apache.http.client.methods.CloseableHttpResponse; ...

    httpclient httpclient.jar

    HttpClient是Apache基金会开发的一个Java库,它为Java程序员提供了一个强大的、可信赖的HTTP协议客户端实现。这个库广泛用于从Web服务器获取数据、发送请求、处理响应等任务,尤其在爬虫、API交互、自动化测试等领域...

    httpclient http客户端

    HttpClient是Apache基金会开发的一个Java库,它为Java程序员提供了一个强大的工具集,用于执行HTTP请求并处理响应。这个库使得在Java应用中实现HTTP通信变得简单,尤其在需要模拟浏览器行为或者进行API测试时非常...

    轻量级C++实现的httpserver和httpclient

    本项目聚焦于轻量级的C++实现的HTTP服务器(httpserver)和HTTP客户端(httpclient),利用mongoose库作为基础,为开发者提供了一种简单且高效的方法来添加HTTP功能,而无需依赖如libcurl这样的大型库。 HTTP(超...

Global site tag (gtag.js) - Google Analytics