当柳上原的风吹向天际的时候...

真正的快乐来源于创造

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
在交易系统的C/S体系中,C只负责数据的输入和显示,相当于MVC中的View部分,S负责数据的操作和持久化,两者是通过WebService进行联系的,具体来说联系的方式是这样:C端将指定S端负责处理的Service类名,具体负责处理的函数名和函数的参数打包成一个XML传送到S端,S端解析后通过反射找到具体的函数进行处理,处理的结果会转化成XML形式的字符串传回。这就是设计梗概一中提到的内容。

如果这个过程交给负责具体业务的程序员自行完成的话,那无疑会给系统带来许多混乱和无序,程序员也无法将主要精力集中在业务上;另外,他们也无需了解每个细节是怎么完成的,他们真正需要的是框架提供好的接口,知道怎么调用取得结果就可以了。他们希望最好能像调用普通函数一样调用S中的方法并取得想要的结果,举例来说,如果客户端需要查询姓名以H开头的所有雇员,他们只需调用一个search函数就能得到查询出来的雇员集合,中间发生的组装请求XML,WebService调用,业务处理,从数据库查询数据,将数据转为XML传回和在客户端解析传回的XML再变成领域对象集合等都应该由框架来完成。这并不过分,而是很合理的需求,就像RMI曾经就是这样做的。

那么,客户端与服务器端的交互会有几种形式呢,从业务上来说无外乎下面五种形式:
1.调用S端的一个函数,只想知道这个函数是否正确运行了。典型例子如对象的删除操作。
2.调用S端的一个函数,想得到函数执行后返回的一个对象。典型例子如对象的添加操作,用户需要取回添加好的对象的ID。
3.调用S端的一个函数,想得到返回对象的集合列表。典型例子如对象的查询。
4.调用S端的一个函数,想得到分页后的某一页对象集合。典型例子如分页查询。
5.调用S端的一个函数,只想得到一个字符串。典型例子如改变一种商品的目录,得到某种商品的介绍文字等。
框架需要做的,就是把这五种形式做成通用的函数提供给负责业务的程序员,让他们仅需要这五个函数就能完成与WebService服务器的交互。这五种形式以第二种最为典型,也最为基础,完成了它其它的就可以依样画葫芦,下面请看具体过程:

首先,规定具体函数的形制为
public static BaseDomainObj fetchObject(String url,String serviceName,String mothodName,String[] args,Class<?> cls);
公有静态自不必说,BaseDomainObj是客户端领域对象的基类,fetchObject是函数名,接下来是四个参数,前三个分别是WebService所在URL,服务器上服务类注册在Spring上下文中的beanName,服务类具体的方法名,最后一个是取得对象的类型,在函数体中,会根据类型用反射生成一个实例,再通过实例的FromXML方法给实例的属性赋值,完成后就得到了负责业务的程序员想要的结果。

其次,fetchObject内部需要做的事情有:
1.将serviceName,mothodName,args三项组合成一段XML文本。此项工作由WSRequest类完成。
2.向位于url上的WebService服务器端发起请求,获得返回的文本。此项工作由WSInvoker类来完成。
3.将返回的文本转化出来,这一步是要检测服务器端函数执行是否顺畅,有无抛出异常等。因为服务器端如果发生异常是无法通过WebService传回的,只能变成文本后回传,那么客户端就需要解析一次,有问题就报出来,没问题再往下走。这一步是坚决不能忽视的。
4.通过反射得到对象,此时对象的属性还是原始状态,需要再通过反射注入相应的值,最后,客户端需要的对象就产生了。

具体的过程请参考下面的代码:
/**
 * 调用远程WebService端,将返回的XML转化为一个对象,最终返回.这种调用的典型例子是getById,add等
 * 
@param url WebService所在URL
 * 
@param serviceName 服务名,此名在appCtx.xml中定义
 * 
@param mothodName 方法名
 * 
@param args 方法的参数
 * 
@param cls 要返回的对象类型
 * 
@return
 
*/
public static BaseDomainObj fetchObject(String url,String serviceName,String mothodName,String[] args,Class<?> cls){
    
// 得到客户端请求XML文本
    WSRequest request=new WSRequest(serviceName,mothodName,args);
    String requestXML
=request.toXML();
    logger.info(
"准备向位于'"+url+"'发送的请求XML为'"+requestXML+"'.");
    
    
try{
        
// 调用远端WebService上的方法,得到返回的XML文本
        WSInvoker invoker=new WSInvoker(url);
        String responseXML
=invoker.getResponseXML(requestXML);
        logger.info(
"得到位于'"+url+"'响应XML为'"+responseXML+"'.");
        
        
// 转化响应
        WSResponse response=new WSResponse(responseXML);
        logger.info(response);
        
        
// 如果在调用过程中如通不过检测而被中断的话
        if(response.isBreaked()){
            String errTxt
="远程方法被中断,具体原因是"+response.getRemark();
            logger.error(errTxt);
            
throw new WSBreakException(errTxt+".(WSE05)");
        }
        
        
// 如果在调用过程中出现异常的话
        if(response.hasException()){
            String errTxt
="调用远程方法返回了异常,具体信息是"+response.getRemark();
            logger.error(errTxt);
            
throw new WSException(errTxt+".(WSE04)");
        }
        
        
try{
            
// 通过反射得到对象
            BaseDomainObj obj= (BaseDomainObj)cls.newInstance();
            
            
// 通过反射得到方法
            Method method = cls.getMethod("fromXML"new Class[] {String.class});
            
            
// 通过反射调用对象的方法
            method.invoke(obj, new Object[] {response.getMethodResonseXML()});
            
            
return obj;
        }
        
catch(Exception ex){
            String errTxt
="无法将"+response.getMethodResonseXML()+"转化为"+cls.getName()+"对象.(WSE06)";
            logger.error(errTxt);
            
throw new WSException(errTxt);
        }
    }
    
catch(MalformedURLException e){
        String errTxt
="无法调用'"+url+"'上的服务,因为它是畸形的.(WSE01)";
        logger.error(errTxt);    
        
throw new WSException(errTxt);
    }
    
catch(XFireRuntimeException e){
        String errTxt
="无法调用'"+url+"'上的服务.(WSE02)";
        logger.error(errTxt);    
        
throw new WSException(errTxt);
    }
    
catch(DocumentException e){
        String errTxt
="无法解析从服务器端'"+url+"'返回的XML文本.(WSE03)";
        logger.error(errTxt);    
        
throw new WSException(errTxt);
    }
}


我们再看看通过关键的注入属性值的fromXML函数做了些什么:
public void fromXML(String xml) throws DocumentException{
    Document doc
=DocumentHelper.parseText(xml);        
    
    Element root
=doc.getRootElement();        
    List
<Element> elms=root.elements();
    
    
for(Element elm:elms){
        
try {
            
// 借助于BeanUtils,给对象的属性赋值
            BeanUtils.setProperty(this,elm.getName(),elm.getText());
        } 
catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}


最后,我们可以看看负责业务的程序员需要书写的代码示例:
public Tmp add(String name,String age,String salary,String picture){
    String url
=CommonUtil.WebService_Url;
    String serviceName
="TmpService";
    String methodName
="add";
    String[] args
=new String[]{name,age,salary,picture};
    
    
try{
        
return (Tmp)WSUtil.fetchObject(url, serviceName, methodName, args, Tmp.class);
    }
    
catch(WSBreakException ex){
        DlgUtil.popupWarningDialog(ex.getMessage());
    }
    
catch(WSException ex){
        DlgUtil.popupErrorDialog(ex.getMessage());
    }
    
    
return null;
}


小结:
一.负责业务程序员不需要了解的细节,框架应该将它们隐藏起来。
二.负责业务程序员需要了解的接口,框架应该使它们尽量简单。
三.框架能做到的,就不该再让负责业务的程序员再重复的发明车轮。
posted on 2010-05-21 00:18 何杨 阅读(190) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: