使用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);
            }
        }
    }
}

你可能感兴趣的:(spring)