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相关参考关键字百度