`

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

分享到:
评论
2 楼 liyang678 2017-12-28  
这样不就得要求 被请求一方必须按您的这种XML格式解析吗。
1 楼 liyang678 2017-12-28  
可以演示一下如何调用吗。不是很明白呢。

相关推荐

    基于Spring MVC的web框架 1.1.11

    工具类数据校验 jsp自定义标签 Spring自定义注解 默认requestMapping 1.1.2 代码生成器 1.1.3 首页修改 dateformat.js 时间参数转换 SpringMVC配置文件集中 快递参数接口 1.1.4 des加解密字符串和文件 1.1.5 redis...

    基于SpringMVC的一个web框架

    工具类数据校验 jsp自定义标签 Spring自定义注解 默认requestMapping 1.1.2 代码生成器 1.1.3 首页修改 dateformat.js 时间参数转换 SpringMVC配置文件集中 快递参数接口 1.1.4 des加解密字符串和文件 1.1.5 redis...

    SpringMVC基础上的web框架

    这个框架是在学习Spring的时候,为了积累学习成果,自己搭建的,一般的系统开发也可以直接使用,包括一个系统开发的基础功能。 以下是当时自己开发时的日志,大致可以说明框架里已有功能 1.0.5 从web项目迁移成maven...

    一个可以直接运行的基于SpringMVC的web框架1.1.12

    工具类数据校验 jsp自定义标签 Spring自定义注解 默认requestMapping 1.1.2 代码生成器 1.1.3 首页修改 dateformat.js 时间参数转换 SpringMVC配置文件集中 快递参数接口 1.1.4 des加解密字符串和文件 1.1.5 redis...

    可以直接运行的基于SpringMVC的web框架示例,也可以直接当公司框架

    工具类数据校验 jsp自定义标签 Spring自定义注解 默认requestMapping 1.1.2 代码生成器 1.1.3 首页修改 dateformat.js 时间参数转换 SpringMVC配置文件集中 快递参数接口 1.1.4 des加解密字符串和文件 1.1.5 redis...

    java命名规范 开发规范

    Spring管理第三方WebService实例bean Jaxws-client配置代码 b. 生成第三方WebService接口文件;(提供系统自动生成) 自动生成代码 c. 页面调用Action请求,Action中注入WebService实例bean; Action对应方法直接...

    xmg-gecco-demo-master.zip

    一个gecco爬虫框架,简单易用,使用jquery风格的选择器抽取元素 支持爬取规则的动态配置和加载 支持页面中的异步ajax请求 支持页面中的javascript变量抽取 利用Redis实现分布式抓取,参考gecco-redis 支持结合Spring...

    JAVA上百实例源码以及开源项目

     //给客户发一个感谢消息,消息驱动Bean必须实现两个接口MessageDrivenBean和MessageListener  在对象创建的过程中将被容器调用,onMessage函数方法接收消息参数,将其强制转型为合适的消息类型,同时打印出消息...

    JAVA上百实例源码以及开源项目源代码

     //给客户发一个感谢消息,消息驱动Bean必须实现两个接口MessageDrivenBean和MessageListener  在对象创建的过程中将被容器调用,onMessage函数方法接收消息参数,将其强制转型为合适的消息类型,同时打印出消息...

Global site tag (gtag.js) - Google Analytics