在某系统开发项目中,需要调用分别部署于不同节点但相同WSDL的WebService接口,为此,通过扩展Spring现有WebService实现机制,实现根据不同路由标识调用不同节点的WebService接口的动态路由。
思路:
通过扩展JaxWsPortClientInterceptor类,实现一个DynamicJaxWsPortProxyFactoryBean类:
- 在Spring实例化阶段,默认不创建WebService的Stub,不单独设置WsdlDocumentUrl;
- 使用多例模式维持一个Service的Map,以路由标识(本项目使用某组织机构代码前缀)为查找Key,在进行WebService 调用时,首先查询以路由标识(先前存储的线程本地变量)为Key的映射中是否已经存在Stub,若不存在,创建并保存到多例映射池中。
/**
*
*/
package jmy.test;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.remoting.jaxws.JaxWsPortClientInterceptor;
/**
*
* <p>
* 标题:DynamicJaxWsPortProxyFactoryBean
* </p>
* <p>
* 描述:
* <ol>
* <li>在Spring实例化阶段,默认不创建WebService的Stub,不单独设置WsdlDocumentUrl;
* <li>使用多例模式维持一个Service的Map,以路由标识(本项目使用组织机构代码前缀)为查找Key,
* 在进行WebService 调用时,首先查询以路由标识(先前就以存储于线程本地变量)为Key的映射
* 中是否已经存在Stub,若不存在,创建并保存到多例映射池中。
* </ol>
* </p>
*/
public class DynamicJaxWsPortProxyFactoryBean extends JaxWsPortClientInterceptor implements FactoryBean<Object> {
private final Logger log = LoggerFactory.getLogger(getClass());
private Map<String, URL> regionWsdls;
private Map<String, Service> wsHolder = Collections.<String, Service>synchronizedMap(new HashMap<String, Service>());
public DynamicJaxWsPortProxyFactoryBean(Map<String, URL> regionWsdls) {
this.regionWsdls = regionWsdls;
}
/**
* 直接抛出异常,动态WebService端口代理不允许设置wsdlDocumentUrl。
* @throw IllegalAccessError
* @see org.springframework.remoting.jaxws.LocalJaxWsServiceFactory#setWsdlDocumentUrl(java.net.URL)
*/
@Override
public void setWsdlDocumentUrl(URL wsdlDocumentUrl) {
throw new IllegalAccessError("动态WebService端口代理不允许设置wsdlDocumentUrl");
}
/**
* 动态查找服务,所以setLookupServiceOnStartup(false)
* @see org.springframework.remoting.jaxws.JaxWsPortClientInterceptor#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
super.setLookupServiceOnStartup(false);
super.afterPropertiesSet();
}
/* (non-Javadoc)
* @see org.springframework.remoting.jaxws.JaxWsPortClientInterceptor#isPrepared()
*/
@Override
protected boolean isPrepared() {
return false;//要求父类永远调用prepare()方法
}
/* (non-Javadoc)
* @see org.springframework.remoting.jaxws.LocalJaxWsServiceFactory#createJaxWsService()
*/
@Override
public Service createJaxWsService() {
String currentRegion = RegionRouter.getRegion();
log.debug(String.format("创建路由区域为【%s】的WebService", currentRegion));
super.setWsdlDocumentUrl(regionWsdls.get(currentRegion));
Service svc = super.createJaxWsService();
this.wsHolder.put(currentRegion, svc);
return svc;
}
/* (non-Javadoc)
* @see org.springframework.remoting.jaxws.JaxWsPortClientInterceptor#getJaxWsService()
*/
@Override
public Service getJaxWsService() {
String currentRegion = RegionRouter.getRegion();
Service obj = wsHolder.get(currentRegion);
log.debug(String.format("获取路由区域为【%s】的WebService,是否获取到?【%s】", currentRegion, obj != null));
return obj;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public Object getObject() throws Exception {
// Build a proxy that also exposes the JAX-WS BindingProvider interface.
ProxyFactory pf = new ProxyFactory();
pf.addInterface(getServiceInterface());
pf.addInterface(BindingProvider.class);
pf.addAdvice(this);
return pf.getProxy(getBeanClassLoader());
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
/**
* 本FactoryBean以多例模式实现对象创建。
*
* @return false
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return false;
}
}