您的位置:首页 > 编程语言 > Java开发

Stub方式接口化封装axis2 webservice:实现衍生对象与原始对象的相互转换

2015-09-22 18:49 633 查看
本文适合正做做涉及webservice项目开发,对axis2 webservice有一定了解的开发者。

在使用axis2 webservice时,对于初始学者,最方便的方式就是就wsdl2java工具生成Stub代码,然后就可以方便的调用Stub代码提供的类,像本地方法一样调用webservice接口。

我们知道,axis2生成的webservice接口可以很方便的传递复杂类型对象(对应于WSDL中的ComplexType,复杂类型对象指用户定义的类)或对象数组。

axis2对集合对象(Collection)也是支持的,但我的项目中没有用到所以没有深入研究

原始对象和衍生对象

axis2提供的wsdl2java工具生成的Stub代码(java)会在Stub类下为每个在webservice接口中用到ComplexType生成对应的Class,比如:

你在webservice接口中用到了一个com.facesdk.FRect类,

那么你生成的Stub代码中就会有一个同名的类生成:net.gdface.service.client.FaceDbServiceStub$FRect(这里有$符号,显然它是在名为FaceDbServiceStub的Stub类中的嵌入类)

虽然类名完全一样,但这两个FRect是没有任何继承关系的两个类。为了便于描述,我们把com.facesdk.FRect称为原始类,而相对的把net.gdface.service.client.FaceDbServiceStub#FRect称为对应的Stub衍生类,把它们的对象称为原始对象衍生对象

也就是说,如果你用Stub方式调用webservice接口,那么涉及到使用FRect对象做参数的方法都得用它的衍生对象(net.gdface.service.client.FaceDbServiceStub#FRect)作为参数,同时涉及到返回类型为FRect的方法,其返回的对象也是衍生对象,而不是原始对象(com.facesdk.FRect)。

如果你只是简单的用Stub方式调用webservice,那么这没有任何问题,但是如果你有更高的要求,想让webservice接口能被更方便的使用,想把webservice接口重新封装,让它完全透明化,接口化,在调用时使用原始对象做参数,就需要有办法把在调用时把原始对象转换成衍生对象,并把返回值从衍生对象转换为原始对象。这就是本文标题的重点:衍生对象与原始对象的相互转换

应用场景

我现在正做的一个项目,就遇到了上面的问题:

与一般的应用项目不同,我们这个项目概括说起来是一个开发包,提供给第三方项目开发用户使用,webservice是一个重要的接口,提供了大约70多个port(方法),涉及到十多个ComplexType,如果只是向用户提供裸的webservice接口,那么用户使用这些接口就需要先具备webservice的相关知识,要学会webservice接口调用的方法,这无疑会增加用户的学习成本(话说webservice的裸接口用起来还是不像普通方法那么方便,开发过程中我经常就因为没发现少填一个参数,而在调用时出了问题,查找原因挺麻烦,浪费不少时间)。

为了增加接口的易用性,我们实现了(基于于axis2的Stub方式、RPC方式和基于KSoap的Stub方式)webservice的接口化封装,在webservice接口之止又封装了一层接口,将所有方法的webservice调用全部实现,所有webservice方法对调用方以java接口的方式呈现,这样第三方用户在使用我们的开发包时,就可以像调用任何一个普通的API一样使用webservice,不用再关心webservice调用的实现细节,实现完全的透明化。

代码实现

webservice接口封装

下面这个代码片段,就是用基于Stub方式实现的一个webservice接口封装方法,代码中的注释描述了封装的细节。

public  CodeInfo[] detectAndGetCodeInfo (byte[] imgData,int faceNum,FRect detectRectangle) throws ImageError,NotFaceDetected{
//注意这里方法定义中参数detectRectangle是原始类型,返回的对象数组的compontentType也是原始类型
//另外方法申明抛出的异常也都是原始异常对象(在WSDL定义中异常也是ComplexType)
try{
//webservice request对象实例化
net.gdface.service.client.FaceDbServiceStub.Sdk_detectAndGetCodeInfo m=new net.gdface.service.client.FaceDbServiceStub.Sdk_detectAndGetCodeInfo();
//下面三个set方法调用都是填充参数
m.setImgData(new DataHandler(new ByteArrayDataSource(imgData)));
m.setFaceNum(faceNum);
//在填充detectRectangle参数对象时,调用getStubObject方法把原始对象转换成衍生对象
m.setDetectRectangle((net.gdface.service.client.FaceDbServiceStub.FRect)getStubObject(detectRectangle));
//同步方式调用Stub对应的方法(port)并获取返回结果
net.gdface.service.client.FaceDbServiceStub.CodeInfo[] res= stubProvider.getStub().sdk_detectAndGetCodeInfo(m).get_return();
if (res == null || (res.length == 1 && res[0] == null))
return  new CodeInfo[0];
else
//调用simpleBeanConvert方法将返回的衍生对象数组转换为原始对象数组
return Axis2Utilits.simpleBeanConvert(res, CodeInfo[].class);
}catch(AxisFault e){
throw new RuntimeException(e);
}catch(RemoteException e){
throw new RuntimeException(e);
}catch(net.gdface.service.client.FaceDbServiceServiceRuntimeException e){
//调用convertStubException方法将返回的衍生异常对象转换为原始的异常对象
throw new RuntimeException(Axis2Utilits.convertStubException(e, ServiceRuntime.class));
}catch(net.gdface.service.client.FaceDbServiceNotFaceDetectedException e){
throw Axis2Utilits.convertStubException(e, NotFaceDetected.class);
}catch(net.gdface.service.client.FaceDbServiceImageErrorException e){
throw Axis2Utilits.convertStubException(e, ImageError.class);
} finally {
try {
this.stubProvider.getStub()._getServiceClient().cleanupTransport();
}catch (AxisFault e) {
throw new RuntimeException(e);
}
}
}


衍生对象与原始对象的相互转换

从上面的代码可以看出衍生对象与原始对象的相互转换的实现细节在getStubObject,simpleBeanConvert,convertStubException三个方法中,我们只看最重要的simpleBeanConvert就可以明白转换的实现方式了:

/**
* 简单对象转换,支持JavaBean,和数组,出错则抛出异常
*
* @param t1
*            源对象
* @param c2
*            目标类
* @return 返回目标类对象
* @throws AxisFault
*/
@SuppressWarnings({ "unchecked" })
public static final <T1, T2> T2 simpleBeanConvert(T1 t1, Class<T2> c2) throws AxisFault {
Object srcobj = t1;
if (null == t1)
return null;
try{

if (java.util.Collection.class.isAssignableFrom(t1.getClass())
|| java.util.Collection.class.isAssignableFrom(c2)) {
throw new AxisFault("DON'T SUPPORT Collection");
//其实多做些研究,是可以支持Collecion的,
//只是我们的项目中没有在webservice中使用Collection这么复杂的对象做参数,
//而是用对象数组,所以没有深入研究。
}
OMElement element = null;
//将对象序列化转为xml(OMElement)
if (srcobj.getClass().isArray()) {//数组对象区分处理
String className = srcobj.getClass().getComponentType().getSimpleName();
element = BeanUtil.getOMElement(new QName("root"), (Object[]) srcobj, new QName(className), false, null);
} else {//一般对象
element = BeanUtil.getOMElement(new QName("root"), new Object[] {srcobj}, new QName(srcobj.getClass().getSimpleName()), false, null).getFirstElement();
}
//调用 processObject方法将xml对象(OMElement)反序列化转为指定类的对象
return (T2) BeanUtil.processObject(element, c2, null, srcobj.getClass().isArray(), new DefaultObjectSupplier(),
null);
}finally{
}
}


从上面的代码就可以看出,这个方法实际上就是利用了axis2自身提供的org.apache.axis2.databinding.utils.BeanUtil工具类的getOMElement和processObject两个方法来实现衍生对象与原始对象的相互转换。

方法名作用
getOMElement把一个源对象或对象数组序列化为xml对象(org.apache.axiom.om.OMElement)
processObject把一个xml对象反序列化为指定的目标类对象
只要源对象目标类对象具有相同名称的属性方法,并不要求它们之间有继承关系,

而xml对象(org.apache.axiom.om.OMElement)在这个过程中直到了中间介质的作用。

后记

看到这里,对commons-beanutils熟悉的朋友会说了:

既然只是对象属性复制,为什么不用公共jar包commons-beanutils-x.x.x.jar提供的org.apache.commons.beanutils.BeanUtils#**copyProperties(Object

dest, Object orig)**方法实现呢?不是更方便?而且不需要xml对象这个中间介质多,效率也高啊!

这个问题提得很好,说实话,我也纠结过这个方案的效率问题。

的确,xml对象(org.apache.axiom.om.OMElement)作为中间介质两次转换的确从程序效率角度不好看,但这是目前我想到的最方便的方法,在我实现了上面的方案之后,也想到过用BeanUtils的copyProperties方法,但仔细想想,发现并不简单:

如果要复制的对象属性都是简单数据类型(int,long,String,Date….)那么copyProperties的确是能胜任的,一行代码就搞定了。

但是如果要复制的对象属性中包有复杂对象(complexType)或复制对象数组,那么这些类在Stub代码也有对应的衍生类,

所以,原对象和目标对象即使有相同的属性名,每个属性的get和set方法的参数类型/返回类型也是不同的,只用一次调用copyProperties是不能准确的复制的,会抛出异常。

当然并不是说使用copyProperties的方案完全不可行,一定要使用copyProperties方案,可以使用递归方式写一个方法(假定名为copy)对每个成员对象进行判断如果是简单类型就直接调用copyProperties进行复制,如果是复杂对象或对象数组再调用copy。。。好吧,我就想到这儿,我已经觉得挺麻烦的了。算了,我就没有再深入想下去,如果你对效率有更高的要求,可以沿着这个想法继续研究下喽。

要是搞粗来,表忘了告诉我结果哦:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息