當柳上原的風吹向天際的時候...

          真正的快樂來源于創造

            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 何楊 閱讀(199) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 资兴市| 清涧县| 海盐县| 乐山市| 黑水县| 武汉市| 永泰县| 瑞丽市| 珲春市| 德昌县| 西贡区| 宜兴市| 苍南县| 潮州市| 伽师县| 隆尧县| 滦平县| 淅川县| 信丰县| 将乐县| 通化市| 澎湖县| 平凉市| 通海县| 常德市| 调兵山市| 六安市| 图木舒克市| 永定县| 汉源县| 玛多县| 青岛市| 福海县| 广东省| 札达县| 牙克石市| 宣威市| 东乡族自治县| 余江县| 竹山县| 台前县|