换一种口味实现 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不同,该类被配置为SpringBean后,返回的不是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];
相关推荐
似水流年梦 2019-12-19
bearhoopIT之道 2020-08-16
Kakoola 2020-08-01
凯哥Java 2020-07-04
haidaoxianzi 2020-07-04
delmarks 2020-06-28
aNian 2020-06-16
qingjiuquan 2020-06-07
fraternityjava 2020-06-04
明瞳 2020-06-04
HappyHeng 2020-05-28
88473166 2020-05-14
zkwgpp 2020-05-07
oXiaoChong 2020-05-07
ChainDestiny 2020-05-07
doomvsjing 2020-05-07
xuMelon 2020-05-04