首页 > 代码库 > WebServcie结合Spring结合动态代理进行抽象封装以及性能优化

WebServcie结合Spring结合动态代理进行抽象封装以及性能优化

 

技术分享

webService抽象客户端封装、动态代理提升使用性能

1. 什么是webService
  webService就是在web上提供非相关系统与系统之间进行数据交互的一种服务。通过实现定义好的wsdl借口配置文件,进行约定以及调用。
在企业的内部系统中很常见,尤其是比较大型的企业,有数十种内部使用的系统,之间的通信基本上都是使用webService。
通俗点说就是:你要调用别人的服务,你就通过wsdl生成客户端代码,方便进行直接调用。
你需要被别人调用,你就通过wsdl生成服务端代码,方便对方系统进行推送调用。
2. 如何实例化
  通过JaxWsProxyFactoryBean传入(wsdl地址、用户名、密码、接口类)就能够远端调用实例。
  获得实例调用方法,方法结束整个流程结束。
3. 如何封装以及提升性能
  一次获得实例后,设置远端参数,实现延长失效。创建动态代理的客户端实例,供系统调用。
4. 由于每次调用的接口可能不同,但是实际上面的流程都是通过wsdl地址、以及接口类,获取对应的实例。
  所以可进行封装,接口类名通过泛型的方式传入、而公开一个抽象类,其中一个抽象方法传入对应的wsdl地址。这样讲两个动态的参数传入,使用泛型的形式生成对应的客户端。

  设置客户端的链接参数,改变失效时间。通过实例创建动态代理,公开给别人调用。

思路:

1. 通过Spring框架中的FactoryBean对象传入对应的接口泛型,进行上层封装

2. 定义抽象类,声明抽象方法getWSServiceUrl获得每个客户端对应的实例地址。

3. @PostConstruct通过容器启动的时候加载对应的JaxWsProxyFactoryBean工厂对象

4. 创建客户端的时候修改客户端映射以及策略,允许分块传输、设置长连接时长

5. 创建客户端代理类,调用代理的时候才调用真正的客户端实例

实现:

  1 package helloworld;
  2 
  3 import java.lang.reflect.InvocationHandler;
  4 import java.lang.reflect.Method;
  5 import java.lang.reflect.ParameterizedType;
  6 import java.lang.reflect.Proxy;
  7 import java.lang.reflect.Type;
  8 
  9 import javax.annotation.PostConstruct;
 10 
 11 import org.apache.commons.lang3.ArrayUtils;
 12 import org.apache.commons.lang3.StringUtils;
 13 import org.apache.cxf.endpoint.Client;
 14 import org.apache.cxf.frontend.ClientProxy;
 15 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
 16 import org.apache.cxf.transport.http.HTTPConduit;
 17 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 18 import org.slf4j.Logger;
 19 import org.slf4j.LoggerFactory;
 20 import org.springframework.beans.factory.FactoryBean;
 21 
 22 /**
 23  * 描述: 定义一个抽象类,实现工厂Bean方法,使用泛型来定义传入的接口类,未定义的接口类
 24  * 调用方法,使用getClient方法来调用对应的WebService
 25  */
 26 public abstract class AbstractCxfClient<T> implements FactoryBean<T> {
 27 
 28     private static final Logger logger = LoggerFactory.getLogger(AbstractCxfClient.class); // 日志处理工具类
 29 
 30     private JaxWsProxyFactoryBean factory; // apache webservice FactoryBean
 31 
 32     private volatile T client; // 接口客户端泛型类
 33 
 34     private Class<?> genericClazz; // Class<?>是通配泛型,?可以代表各种类型
 35 
 36     /**
 37      * 方法说明:初始化webservice客户端方法 关于在spring 容器初始化 bean 和销毁前所做的操作定义方式有三种
 38      * 通过@PostConstruct 和@PreDestroy 方法 实现初始化和销毁bean之前进行的操作
 39      * 
 40      * @throws Exception
 41      */
 42     @PostConstruct
 43     void initial() throws Exception { // 初始化操作
 44         String url = getWSServiceUrl(); // 服务的地址
 45         if (StringUtils.isBlank(url)) // 地址为空,报错
 46         {
 47             throw new Exception("webservice client 初始化失败。" + this.getClass().getName());
 48         }
 49         Type type = this.getClass().getGenericSuperclass(); // 获取当前类的父类,或者说就是抽象类
 50 
 51         if (type instanceof ParameterizedType) { // 是否是具有泛型的抽象类
 52             ParameterizedType pt = (ParameterizedType) type;// 获取对应的存储泛型类的类
 53             Type[] typeArr = pt.getActualTypeArguments(); // 获取泛型类名称的字符串数组
 54             if (ArrayUtils.isEmpty(typeArr)) { // 如果为空,说明没有传入泛型
 55                 throw new RuntimeException("所要调用的服务端接口不能为空" + this.getClass().getName());
 56             }
 57             genericClazz = Class.class.cast(typeArr[0]); // 由于代码写明了只有一个泛型类名字
 58             factory = new JaxWsProxyFactoryBean(); // 创建工厂
 59             factory.setServiceClass(genericClazz); // 设置对应的服务的类名
 60             String userName = this.getUserName(); // 设置webServie用户名
 61             if (StringUtils.isNotBlank(userName)) {
 62                 factory.setUsername(userName);
 63             }
 64             String possword = this.getPossword(); // 设置webService密码
 65             if (StringUtils.isNoneBlank(possword)) {
 66                 factory.setPassword(possword);
 67             }
 68             client = null; // 重置客户端
 69             factory.setAddress(url); // 设置对应的url连接
 70         } else { // 报错无效的客户端实例
 71             throw new RuntimeException("无效的客户端实例" + this.getClass().getName());
 72         }
 73     }
 74 
 75     /**
 76      * 方法说明:<br>
 77      * 获取发发送实例获取代理后的客户端实例
 78      * 
 79      * @param client
 80      * @return 代理后的客户端实例
 81      */
 82     private void httpClientPolicy(T client) {
 83         Client proxy = null; // 端点客户端
 84         HTTPConduit conduit = null; 
 85         HTTPClientPolicy policy = null;
 86         try {
 87             // 通过实例获取对应的客户端代理
 88             proxy = ClientProxy.getClient(client);
 89             conduit = (HTTPConduit) proxy.getConduit(); // 获取静态映射
 90             policy = new HTTPClientPolicy(); // 创建策略
 91             policy.setAllowChunking(false);  // 设置允许分块传输
 92             policy.setConnectionTimeout(60000);    // 连接服务器超时时间一分钟
 93             policy.setReceiveTimeout(60000);// 等待服务器响应超时时间一分钟
 94             conduit.setClient(policy); // 将策略设置进端点
 95         } catch (Throwable t) {
 96             throw new RuntimeException(t.getMessage());
 97         }
 98     }
 99 
100     /**
101      * 2016年3月21日 601008方法说明: CXF客户端一次获取,多次使用,方法调用异常,重新获取客户客户端
102      * 
103      * @return T CXF客户端实例
104      */
105     @SuppressWarnings("unchecked")
106     public T getClient() {
107         // 如果不存在对应的客户端,才进行客户端远端获取,如果存在的话,就直接使用
108         if (null == client) {
109             synchronized (this) {
110                 if (null == client) {
111                     try {
112                         // 获取客户端
113                         client = (T) factory.create();
114                         // 设置真实客户端时长
115                         httpClientPolicy(client);
116                         // 创建客户端动态代理(调用方法通过代理调用)
117                         client = (T) (new CxfInvocationHandler(client)).getProxy();
118                     } catch (Throwable t) {
119                         client = null;
120                         throw t;
121                     }
122                 }
123             }
124         }
125         return client;
126     }
127 
128     @Override
129     public T getObject() throws Exception {
130         return getClient();
131     }
132 
133     @Override
134     public Class<?> getObjectType() {
135         return genericClazz;
136     }
137 
138     @Override
139     public boolean isSingleton() {
140         // 使用单例模式
141         return true;
142     }
143 
144     /**
145      * 2016年3月12日 wulonghuai方法说明:
146      * 
147      * @return String webservice wsdl路径 客户端必须实现这个方法
148      */
149     public abstract String getWSServiceUrl();
150 
151     public String getUserName() { // 暂时调用的webService都是不用用户名以及用户密码的,所以暂时返回空
152         return null;
153     }
154 
155     public String getPossword() { // 暂时调用的webService都是不用用户名以及用户密码的,所以暂时返回空
156         return null;
157     }
158 
159     // CXF代理对象的实现类
160     private class CxfInvocationHandler implements InvocationHandler {
161 
162         // 目标对象
163         private Object target;
164 
165         /**
166          * 构造方法
167          * 
168          * @param target
169          *        目标对象
170          */
171         public CxfInvocationHandler(Object target) {
172             super();
173             this.target = target;
174         }
175 
176         /**
177          * 执行目标对象的方法
178          */
179         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
180             try {
181                 return method.invoke(target, args);
182             } catch (Throwable t) {
183                 // 动态代理调用对方的方法
184                 client = null;// 服务调用异常,客户端置空,方便后面重新获取客户端
185                 throw t;
186             }
187         }
188 
189         public Object getProxy() {
190             // 传入接口、传入类加载器、传入当前对象
191             return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
192                     target.getClass().getInterfaces(), this);
193         }
194     }
195 }

参考资料:

  cxf基础见官网:http://cxf.apache.org/

  wsdl可通过官网工具生成、也能够通过eclipse直接生成

  spring相关参考关键字百度

 

WebServcie结合Spring结合动态代理进行抽象封装以及性能优化