基于注解 + 反射 + 动态代理
先上代码:
@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];
分享到:
相关推荐
hp实现httpclient类示例,需要的朋友可以参考下,buffer 获取返回的字符串,referer 设置 HTTP_REFERER 的网址,response 服务器响应的 header 信息,request 发送到服务器的 header 信息。
主要介绍了php实现httpclient类示例,需要的朋友可以参考下,buffer 获取返回的字符串,referer 设置 HTTP_REFERER 的网址,response 服务器响应的 header 信息,request 发送到服务器的 header 信息。
为帮助java初学者,我特意整理了一下用java实现httpClient的相关代码,其中包括客户端请求https服务器,直接解压放在eclipse即可查看运行,希望对想了解这方面的童鞋有帮助。
主要介绍了java实现HttpClient异步请求资源的方法,实例分析了java基于http协议实现异步请求的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
springmvc实现httpclient调用服务
使用c#实现的HttpClient拼接multipart/form-data形式参数post提交数据,包含图片内容,有需要的可以下载,希望能帮到有需要的人,
使用HttpClient4.5实现https请求忽略SSL证书验证工具类
HttpClient的一个封装, HttpClient的一个封装
用HttpClient实现文件下载需要满足:有一台web服务器,要下载的源文件需存放在web服务器的特定目录下。通过发起http请求实现文件下载功能。
后台程序员将数据封装成json 怎么取出其中的数据 这里面包括所需要的jar包以及实现类
通过HttpClient实现远程下载,本例子通过java代码实现
HttpClient实现POST GET和文件下载 HttpClient实现POST GET和文件下载
httpclient
使用mongoose实现了httpserver,httpclient,websocketserver
* 使用 GetMethod 来访问一个 URL 对应的网页,实现步骤: 1:生成一个 HttpClinet 对象并设置相应的参数。 * 2:生成一个 GetMethod 对象并设置响应的参数。 3:用 HttpClinet 生成的对象来执行 GetMethod 生成的Get ...
java实现http接口服务端、客户端代码例子。使用jar(commons-codec-1.3.jar、commons-httpclient-3.0.1.jar、commons-logging-1.1.1.jar)
java中跨项目使用httpclient实现数据交换
httpClient完整封装获取网页信息、数据的代码+httpclient.jar