使用HttpClient、注解、动态代理、Spring的Bean后处理器实现Http消息发送


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"
default-lazy-init="false">

<!-- 加载资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />

<!-- 配置数据源 dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>

<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="modify*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="select*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="bizMethods"
expression="execution(* com.it.springbiz.*.service.*.*(..)) or execution(* com.it.springbiz.*.*.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods" />
</aop:config>

<!-- 编程式事务模板来完成Spring的编程式事务 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>

<!-- jdbcTemplate 用来操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- namedParameterJdbcTemplate支持命名参数特性 简化数据库操作-->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg index="0" ref="dataSource"></constructor-arg>
</bean>

<!-- Spring 扫描使用注解的包路径 -->
<context:component-scan
base-package="com.it" />

<!-- 提供注解接口服务的注册功能 -->
<bean id="httpServiceProxy" class="com.iteye.http.HttpClientBeanPostProcessor">
</bean>

<import resource="classpath*:/bean/*-bean.xml" />

</beans>




package com.iteye.http.annotation;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target(ElementType.METHOD)
@Retention(RUNTIME)
public @interface HttpMethod {
String requestUrl();

/**
* 操作编码
*/
String operation();

/**
* 编码
*/
String enCode() default "UTF-8";

/**
* 连接超时时间
*/
int reqConnectTimeout() default 1000;

/**
* 请求返回超时时间
*/
int repTimeout() default 5000;

}






package com.iteye.http.annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
*
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface HttpService {
/**
* 服务代码
*
* @return
*/
String serviceCode();

/**
* 请求方系统代码
*
* @return
*/
String appCode();
}






package com.iteye.http.annotation;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* 在需要注入HttpService的类中的字段或者set方法上添加该注解,后置处理器会处理使用该注解的接口为其生成代理类<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
public @interface HttpWired {

}





package com.iteye.http.dto;

/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class RequestDto {
/**
* 系统编码
*/
private String appCode;

/**
* 服务编码
*/
private String serviceCode;

/**
* 操作编码
*/
private String operation;

/**
* 编码
*/
private String enCode;

/**
* 连接超时时间
*/
private int reqConnectTimeout;

/**
* 请求返回超时时间
*/
private int repTimeout;

/**
* 报文体内容
*/
private String requestBody;

public String getRequestBody() {
return requestBody;
}

public void setRequestBody(String requestBody) {
this.requestBody = requestBody;
}

public String getAppCode() {
return appCode;
}

public void setAppCode(String appCode) {
this.appCode = appCode;
}

public String getServiceCode() {
return serviceCode;
}

public void setServiceCode(String serviceCode) {
this.serviceCode = serviceCode;
}

public String getOperation() {
return operation;
}

public void setOperation(String operation) {
this.operation = operation;
}

public String getEnCode() {
return enCode;
}

public void setEnCode(String enCode) {
this.enCode = enCode;
}

public int getReqConnectTimeout() {
return reqConnectTimeout;
}

public void setReqConnectTimeout(int reqConnectTimeout) {
this.reqConnectTimeout = reqConnectTimeout;
}

public int getRepTimeout() {
return repTimeout;
}

public void setRepTimeout(int repTimeout) {
this.repTimeout = repTimeout;
}

}






package com.iteye.http;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.iteye.http.annotation.HttpService;
import com.iteye.http.annotation.HttpWired;

/**
* 〈通过BeanPostProcessor后置处理器为代理接口生成代理类〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class HttpClientBeanPostProcessor implements BeanPostProcessor {

/**
* 服务接口Class与代理对象的关系列表
*/
private Map<String, Object> httpServiceProxys = new HashMap<String, Object>();

/**
*
* 此方法在每个Bean实例化、依赖注入完成之后执行,在调用afterPropertiesSet(实现InitializingBean接口)、init-method方法前执行 <br>
* 〈在这里为HttpService代理接口生成代理类,这样调用代理接口的方法其实调用的是代理类的方法,在代理类方法中完成HttpClient发送xml报文的代码封装〉
*
* @param bean
* @param beanName
* @return
* @throws BeansException
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 为HttpService代理接口生成代理类
wireHttpServiceBean(bean, beanName);
return bean;
}

/**
*
* 为HttpService代理接口生成代理类 <br>
* 〈功能详细描述〉
*
* @param bean
* @param beanName
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
private void wireHttpServiceBean(Object bean, String beanName) {
// 扫描当前处理的Bean中的所有set方法使用了@HttpWired注解属性
Class<?> clazz = bean.getClass();

BeanInfo beanInfo = null;
try {
beanInfo = java.beans.Introspector.getBeanInfo(clazz);
} catch (IntrospectionException e) {
throw new RuntimeException("Wiring bean=[" + beanName + "] meet error.", e);
}

// 获取当前Bean中的所有属性描述列表
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

// 如果没有提供set方法
Method writeMethod = propertyDescriptor.getWriteMethod();
if (writeMethod == null) {
continue;
}

// 如果set方法上没有使用@HttpWired注解
if (!writeMethod.isAnnotationPresent(HttpWired.class)) {
continue;
}

// 获取注解接口的类型
Class<?> httpServiceClass = propertyDescriptor.getPropertyType();
// 获取代理接口对象
Object httpServiceProxy = getHttpServiceProxy(httpServiceClass);

try {
// 调用set方法为代理接口注入代理对象
writeMethod.invoke(bean, httpServiceProxy);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

/**
*
* 获取代理接口对象 <br>
* 〈功能详细描述〉
*
* @param httpServiceClass
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
private Object getHttpServiceProxy(Class<?> httpServiceClass) {
// 该接口是否使用了@HttpService注解
if (!httpServiceClass.isAnnotationPresent(HttpService.class)) {
throw new RuntimeException(httpServiceClass + "该接口上没有使用@HttpService注解");
}

String key = httpServiceClass.hashCode() + httpServiceClass.getName();
Object proxyObject = httpServiceProxys.get(key);
// 判断该代理对象是否已经存在,因为不同的Bean中都会注入该@HttpService注解对象,防止重复创建代理对象
if (proxyObject != null) {
return proxyObject;
}

HttpServiceProxy httpServiceProxy = new HttpServiceProxy();
// 设置代理的接口
httpServiceProxy.setHttpServiceClass(httpServiceClass);
// 生成代理对象
proxyObject = httpServiceProxy.createHttpServiceProxy();

httpServiceProxys.put(key, proxyObject);

return proxyObject;
}

/**
*
* 此方法在每个Bean实例化、依赖注入、在调用afterPropertiesSet(实现InitializingBean接口)、init-method方法之后执行: <br>
* 〈功能详细描述〉
*
* @param bean
* @param beanName
* @return
* @throws BeansException
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

}





package com.iteye.http;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.util.ClassUtils;

/**
* 〈动态代理实现方式〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class HttpServiceDramaticProxy implements InvocationHandler {

/**
* 代理的接口类型
*/
private Class<?> httpServiceClass;

public void setHttpServiceClass(Class<?> httpServiceClass) {
this.httpServiceClass = httpServiceClass;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
System.out.println(args[0]);
return "1111";
}

public Object createHttpServiceProxy() {
// 实现的接口
Class<?>[] interfaces = { httpServiceClass };

// 通过动态代理为代理接口生成代理对象
Object proxy = Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), interfaces, this);
return proxy;
}
}





package com.iteye.http;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.util.ClassUtils;

import com.iteye.http.annotation.HttpMethod;
import com.iteye.http.annotation.HttpService;

/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class HttpServiceProxy implements MethodInterceptor {

/**
* 代理的接口类型
*/
private Class<?> httpServiceClass;

public void setHttpServiceClass(Class<?> httpServiceClass) {
this.httpServiceClass = httpServiceClass;
}

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {

// 获取接口调用的方法
Method method = invocation.getMethod();
// 方法上是否有HttpMethod注解
if (!method.isAnnotationPresent(HttpMethod.class)) {
throw new RuntimeException(method + "方法上没有使用HttpMethod注解");
}

Object[] args = invocation.getArguments();
if (args == null || args.length != 1 || !(args[0] instanceof String)) {
throw new RuntimeException("方法体的参数有且只能有一个,且类型是String");
}
String requestBody = (String) args[0];

HttpMethod httpMethod = method.getAnnotation(HttpMethod.class);

HttpService httpService = httpServiceClass.getAnnotation(HttpService.class);
// 获取请求的报文
String requestXml = getRequestXml(httpService, httpMethod, requestBody);

// 以post方式发送请求报文并返回响应信息
String responseXml = HttpClientUtils.postHttpRequest(requestXml, httpMethod.requestUrl(),
httpMethod.reqConnectTimeout(), httpMethod.repTimeout(), httpMethod.enCode());

return responseXml;
}

/**
*
* 获取请求的报文 <br>
* 〈功能详细描述〉
*
* @param httpService
* @param httpMethod
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
private String getRequestXml(HttpService httpService, HttpMethod httpMethod, String requestBody) {
String requestXml="<MbfService>"+
"<input1>"+
"<MbfHeader>"+
"<ServiceCode>"+httpService.serviceCode()+"</ServiceCode>"+
"<Operation>"+httpMethod.operation()+"</Operation>"+
"</MbfHeader>"+
"<MbfBody>"+
requestBody+
"</MbfBody>"+
"</input1>"+
"</MbfService>";

return requestXml;
}

public Object createHttpServiceProxy() {
// proxy是一个实现了serviceInterface接口的代理对象,当调用proxy的某个方法的时候,就会调用this的invoke方法
Object proxy = new ProxyFactory(httpServiceClass, this).getProxy(ClassUtils.getDefaultClassLoader());
return proxy;
}

}





package com.iteye.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.SocketTimeoutException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;

/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class HttpClientUtils {
/**
*
* 以Post方式发送Http请求 <br>
* 〈功能详细描述〉
*
* @throws IOException
* @throws ClientProtocolException
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
public static String postHttpRequest(String requestXml, String requestUrl, int reqConnectTimeout, int repTimeout,
String enCode) {
HttpPost httpPost = null;

try {
// 定义HttpPost请求
httpPost = new HttpPost(requestUrl);
// 定义请求实体
HttpEntity requestEntity = new StringEntity(requestXml, enCode);
httpPost.setEntity(requestEntity);

// 定义HttpClient
HttpClient httpClient = new DefaultHttpClient();

HttpParams httpParams = httpClient.getParams();
// 设置Http协议的版本
httpParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
// 设置请求连接超时时间
httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, reqConnectTimeout);
// 设置请求响应超时时间
httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, repTimeout);

// 以post方式发送Http请求
HttpResponse httpResponse = httpClient.execute(httpPost);

// 获取响应实体
HttpEntity responsetEntity = httpResponse.getEntity();
InputStream inputStream = responsetEntity.getContent();

StringBuilder reponseXml = new StringBuilder();
byte[] b = new byte[2048];
int length = 0;
while ((length = inputStream.read(b)) != -1) {
reponseXml.append(new String(b, 0, length));
}

return reponseXml.toString();
} catch (Exception e) {
// 释放请求的连接
if (httpPost != null) {
httpPost.abort();
}

if (SocketTimeoutException.class.isInstance(e)) {
throw new RuntimeException("Http请求响应超时", e);
} else if (ConnectTimeoutException.class.isInstance(e)) {
throw new RuntimeException("Http请求连接超时", e);
} else if (ConnectException.class.isInstance(e)) {
throw new RuntimeException("Http请求异常", e);
} else {
throw new RuntimeException(e);
}
}
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值