<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    真正的快樂來源于創造

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
    一.C/S兩端的任務分離
       考慮到便于信息接收傳遞顯示的因素,交易系統和QQ類似,采用了傳統的C/S模式而不是B/S模式。Client端主要負責取得用戶輸入和數據顯示,而Server端分為DBServer和MsgServer兩個,前者負責數據的持久化,后者負責消息的傳遞。撇開消息服務器MsgServer不談的話,數據傳遞主要發生在Client和DBServer之間。
       
    二.C/S兩端的交互方式
       由于C端只負責數據的輸入和顯示,它必然需要向DBServer端存取數據,這就有一個信息載體和交互方式的問題。C端需要向DbServer(以下簡稱DS)傳遞的信息是多種多樣的,簡單命令行形式的數據肯定不行,類似JSON的線性形式不夠表現樹狀數據,只有XML才有豐富的表現能力,它無論是簡單的線性數據還是復雜的樹狀數據都能容納,有了dom4j或是jdom的幫助,解析起來也很方便。交互方式上,由于C可能在廣域網中,還可能有防火墻的阻擋,這樣Socket長連接就受到一定程度的限制,要是采用WebService問題就解決了,因為WebService的底層協議還是http,也走80端口,不會被防火墻阻擋,這樣,DBServer就成了一臺放置在公網上的WebService服務器,為各個Client提供Webservice服務。

    三.實現WebService的軟件選擇
       備選有Axis1/2和XFire兩種方案,選擇的依據主要是效率。通過一段時間的使用,發現XFire的效率確實比Axis1/2高,估測同等調用只占后者的一半左右。其它的易用性,穩定性等沒有成為選擇依據,因為如果XFire還不行再換其它的軟件也來得及,下面的設計保證了系統不會依賴于特定的WebService端軟件。

    四.WebSevice端的對外接口設計

    WebService的對外端口一般是由一個接口和一個實現類組成,實現類中的函數是具體實現,接口是調用者和實現者共同遵守的規約;一般來說如果客戶端需要一個函數的話,那么服務器端的接口類要定義這個函數,實現類實現這個函數。這樣的方式在交互簡單,數據量小的時侯沒有問題,且使用很方便,但量變引起質變,如果交互復雜,需要的函數眾多,數據量與日俱增的話,問題就來了。其一,這回導致接口類和實現類函數越來越多,體積越來越大,對定位維護修改帶來很大的不變;其二,接口類和實現類常會被修改,而開發人員之間的協同等待甚至沖突就日益增多起來,阻滯了開發效率;其三,也是最重要的,系統的可擴展性缺乏,難以動態維護,即使增加多個服務器分擔負載,也需要手動修改大量的代碼。因此,這種傳統的方式在Demo版過后就被放棄了。
    新的方式采用的單接口設計,即接口類中只定義一個函數,實現類實現這一個函數,其內部采用反射的方式具體調用在Spring上下文中定義好的Service類來取得結果,輸入的參數和返回值都是String,其實質是XML形式的字符串。這樣做的好處是:其一,接口類和實現類從設計開始代碼就處于穩定狀態,以后極少維護,不會越來越大;其二,自然消除了多個開發人員需要修改同一文件的沖突問題;其三,如果服務器負載過重,可以在實現類中根據輸入參數的內容做一個分流,把一些任務分配到其它服務器上去,甚至可以采用前端一個分流服務器,后面一堆負責具體業務的服務器的形式。由于只有一個函數,這樣修改起來也容易得多。事實上,采用了這種方式后,完成各個流程的程序員只負責前端表現輸入,后端的Service類等三個位置的代碼,相互間處于平行狀態,基本沒有交叉,減少了沖突,提高了開發效率。下面是實現類的具體代碼。

    五.WebService端實現類的具體代碼
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    import org.apache.log4j.Logger;
    import org.dom4j.DocumentException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;

    /**
     * 此類中共有方法為WebService對外方法,其它方法為輔助此方法而使用
     *
     * 創建日期:2010-2-9 上午09:19:31
     * 修改時間:2010-2-9 上午09:19:31
     
    */
    public class ServiceImpl implements IService{
        
    // 日志記錄器
        private    static Logger logger = Logger.getLogger(ServiceImpl.class);
        
        
    /**
         * 此函數將逐步進行以下任務
         * 1.在log文件中記錄請求的XML文本
         * 2.解析文本,得到要訪問的類名,方法名,參數
         * 3.使用反射調用類的方法
         * 4.返回結果
         * 
    @throws InstantiationException
         
    */
        
    public String getResponseXML(String requestXML){
            logger.info(
    "接收到客戶端的請求XML文本:"+requestXML);
            
    // 新建一個包裝器
            ResponseXMLPackager packager=new ResponseXMLPackager();
            
            
    try {
                
    // 使用解析器解析請求XML文本
                RequestXMLParser parser=new RequestXMLParser(requestXML);
                
                
    // 從解析器中獲取Service服務類
                packager.setServiceName(parser.getServiceName());
                
                
    // 從解析器中獲取方法名
                packager.setMethodName(parser.getMethodName());
                
                
    // 從解析器中獲取方法參數
                packager.setArgs(parser.getArgs());
                
                
    // 通過Spring得到實例
                Object obj=SpringUtil.getBean(packager.getServiceName());
                logger.info(
    "在Spring上下文配置文件中找到了'"+packager.getMethodName()+"'對應的bean.");
                
                
    // 得到實例對應的類
                Class<?> cls=obj.getClass();
                
                
    // 通過反射得到方法
                Method method = cls.getMethod(packager.getMethodName(), new Class[] {String[].class});
                logger.info(
    "通過反射獲得了'"+packager.getMethodName()+"'對應的方法.");
                
                
    // 通過反射調用對象的方法
                String methodResonseXML=(String)method.invoke(obj,new Object[] {packager.getArgs()});
                logger.info(
    "通過反射調用方法'"+packager.getMethodName()+"'成功.");
                
                
    /**************************
                 * 設置狀態,備注及方法反饋結果
                 *************************
    */
                String remark
    ="成功執行類'"+packager.getServiceName()+"'的方法'"+packager.getMethodName()+"'";
                packager.setStatus(ResponseXMLPackager.Status_Success);
                packager.setRemark(remark);
                packager.setMethodResonseXML(methodResonseXML);     
                
                logger.info(remark);
            }
    catch (DocumentException e) {
                
    // 解析不了從客戶端傳過來的XML文本時
                
                String remark
    ="無法解析客戶端的請求XML文本:"+requestXML+".";
                
                packager.setRemark(remark);
                packager.setStatus(ResponseXMLPackager.Status_CanNotParseRequestXML);
                
                logger.error(remark);
            }
    catch (NoSuchBeanDefinitionException e) {
                
    // Spring找不到bean時            
                String remark="無法在Spring上下文定義文件appCtx.xml中找到id'"+packager.getServiceName()+"'對應的bean.";    
                            
                packager.setRemark(remark);
                packager.setStatus(ResponseXMLPackager.Status_CanNotFoundServiceName);
                
                logger.error(remark);
            }        
            
    catch (NoSuchMethodException e) {
                
    // 找不到方法時
                
                String remark
    =("類'"+packager.getServiceName()+"'中沒有名為 ‘"+packager.getMethodName()+"’的方法,或是此方法非公有函數,或是參數不是字符串數組形式.");            
                
                packager.setRemark(remark);
                packager.setStatus(ResponseXMLPackager.Status_NotFoundSuchMethod);
                
                logger.error(remark);
            }
    catch (IllegalAccessException e) {
                
    // 當訪問權限不夠時
                
                String remark
    =("訪問類'"+packager.getServiceName()+"'中名為 ‘"+packager.getMethodName()+"’的方法非法,可能原因是當前方法(getResponseXML)對該方法的訪問權限不夠.");            
                
                packager.setRemark(remark);
                packager.setStatus(ResponseXMLPackager.Status_CanNotAccessMethod);
                
                logger.error(remark);
            }
    catch (InvocationTargetException e) {
                
    // 當調用的函數拋出異常時
                
                Exception tragetException
    =(Exception)e.getTargetException();
                
                
    if(tragetException instanceof BreakException){
                    
    // 程序中斷,不能繼續進行的情況.比如說用戶沒有操作權限,要找的目標不存在等.
                    packager.setRemark(tragetException.getMessage());
                    packager.setStatus(ResponseXMLPackager.Status_Ng);
                    
                    String remark
    =("執行類'"+packager.getServiceName()+"'中名為 ‘"+packager.getMethodName()+"’的方法時被中斷,原因是:"+tragetException.getMessage()+".");    
                    logger.warn(remark);
                }
                
    else{        
                    
    // 程序運行過程中拋出異常,如空指針異常,除零異常,主鍵約束異常等.
                    String remark=("執行類'"+packager.getServiceName()+"'中名為 ‘"+packager.getMethodName()+"’的方法時,該方法拋出了異常,異常類型為:"+tragetException.getClass().getName()+",異常信息是"+tragetException.getMessage()+".");            
                    
                    packager.setRemark(remark);
                    packager.setStatus(ResponseXMLPackager.Status_MethodThrowException);
                    
                    logger.error(remark);
                }
            }
                    
            
    // 向客戶端返回響應XML文本
            return packager.toXML();
        }
    }


    六.Service類中函數的輸入和輸出
       從上面的代碼可見,客戶端傳過來是一個XML形式的文本,RequestXMLParser類負責從這段文本中解析出具體想調用的配置在Spring上下文中Service類的beanName,類中的具體函數名和函數的參數,然后再用反射的方式調用之。為了調用方便,讓每個Service類的具體參數都是String[] 形式的(現在看如果采用類似JSON的形式更好一點),在內部再獲得其實際數據,這樣,來自客戶端的調用就能順利的到達目的函數中。函數運行完畢后,傳出的也是一個XML形式的字符串,這是為了返回數據的方便,到了客戶端后,再進行解析變成領域對象類示例。下面代碼是一個Service類中函數的例子:
    /**
     * 添加一個Tmp對象到數據庫
     * 
    @param args
     * 
    @return
     * 
    @throws Exception
     
    */
    public String add(String[] args) throws Exception{
        String name
    =args[0];
        
        
    // 同名檢測
        if(hasSameName(name)){
            
    throw new BreakException("已經有和"+name+"同名的對象存在了.");
        }
        
        
    int age=Integer.parseInt(args[1]);
        
    float salary=Float.parseFloat(args[2]);
        String picture
    =args[3];
        
        Tmp tmp
    =new Tmp(name,age,salary,picture);
        dao.create(tmp);
        
        
    return tmp.toXML();



    七.領域對象與XML之間的相互轉化

    由于DB服務器和Client之間傳遞的是XML形式的文本,但內部使用的都是領域對象,那么,中間需要兩次轉化過程。以取得一個Tmp對象為例,在服務器端,dao從數據庫取得記錄后會形成Tmp領域對象的實例,這個實例會轉化成XML傳到客戶端;客戶端得到這段XML文本會把它還原成領域對象。以下代碼闡述了這兩個過程:
    // 服務器端領域對象的基類,它的toXML()函數使得實例轉化為XML,它的子類只要實現changePropertytoXML()這個抽象接口就能得到此項功能。
    public abstract class BaseDomainObj{
        
    // 領域對象的唯一識別標志
        protected long id;
        
        
    // 名稱
        protected String name;
        
        
    // 對象對應的記錄被添加到數據庫的時間(入庫時間)
        protected String addTime;
        
        
    // 對象對應的記錄最近被更新的時間(更新時間)
        protected String refreshTime;
        
        
    // 備注
        protected String remark;
        
        
    // 節點名
        protected String nodeName;
        
        
    // 記錄是否有效,若為false則說明無效,常改變此值來隱藏或是顯示一個對象
        protected boolean valid;
        
        
    /**
         * 無參構造函數
         
    */
        
    public BaseDomainObj(){
            
    this(0);
        }
        
        
    /**
         * 指定id的構造函數
         * 
    @param id
         
    */
        
    public BaseDomainObj(long id){
            
    this.id=id;
            String currTime
    =getCurrTime();
            addTime
    =currTime;
            refreshTime
    =currTime;
            valid
    =true;
            remark
    ="";
        }
        
        
    /**
         * 將對象轉化為XML形式
         * 
    @return
         
    */
        
    public String toXML() {
            StringBuilder sb
    =new StringBuilder();
            
            sb.append(
    "<"+nodeName+">");
            sb.append(
    "<id>"+id+"</id>");
            sb.append(
    "<name>"+name+"</name>");
            sb.append(
    "<addTime>"+addTime+"</addTime>");
            sb.append(
    "<refreshTime>"+refreshTime+"</refreshTime>");
            sb.append(
    "<remark>"+remark+"</remark>");
            sb.append(
    "<valid>"+valid+"</valid>");
            sb.append(changePropertytoXML());        
            sb.append(
    "</"+nodeName+">");
            
            
    return sb.toString();
        }
        
        
    /**
         * 將屬性轉化為XML,強制子類實現
         * 
    @return
         
    */
        
    protected abstract String changePropertytoXML();
        
        
    /**
         * 取得當前時間
         
    */
        
    private static String getCurrTime() {
            Date date 
    = new Date();
            Format formatter 
    = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            
    return formatter.format(date);
        }

        
    /*************************
         * 以下為setter/getter
         ************************
    */
        
        ..
    }

    // 具體的Tmp對象,重點是changePropertytoXML()這個函數。
    public class Tmp extends BaseDomainObj{
        
    // 年齡
        private int age;
        
        
    // 薪水
        private float salary;
        
        
    /**
         * 無參構造函數
         
    */
        
    public Tmp(){
            
    this("",0,0.0f);
        }
        
        
    /**
         * 三參數構造函數
         * 
    @param name
         * 
    @param age
         * 
    @param salary
         
    */
        
    public Tmp(String name,int age,float salary){
            nodeName
    ="Tmp";
            
            
    this.name=name;
            
    this.age=age;
            
    this.salary=salary;
        }
        

        @Override
        
    protected String changePropertytoXML() {
            StringBuilder sb
    =new StringBuilder();
            
            sb.append(
    "<age>"+age+"</age>");
            sb.append(
    "<salary>"+salary+"</salary>");
            
            
    return sb.toString();
        }

        
    /***************************
         * 以下為setter/getter部分
         **************************
    */
        
        
    public int getAge() {
            
    return age;
        }


        
    public void setAge(int age) {
            
    this.age = age;
        }


        
    public float getSalary() {
            
    return salary;
        }


        
    public void setSalary(float salary) {
            
    this.salary = salary;
        }
    }

    這樣,在得到一個Tmp對象的實例后,調用其toXML函數就能得到這個實例的XML形式表現文本。“六”中的函數就是這樣做的。

    傳出的XML文本實例:
    <Tmp>
        
    <id>1</id>
        
    <name>0</name>
        
    <addTime>2010-02-15 23:39:06</addTime>
        
    <refreshTime>2010-02-15 23:39:06</refreshTime>
        
    <remark></remark>
        
    <valid>true</valid>
        
    <age>30</age>
        
    <salary>15000.0</salary>
    </Tmp>


    上面這段文本傳回到客戶端后怎么再把它變成實例呢,有了Apache的BeanUtils包任務就簡單多了。下面請看客戶端的Tmp類及其基類:
    // 客戶端Tmp類:
    public class Tmp extends BaseDomainObj{
        
        
    // 年齡
        private String age;
        
        
    // 薪水
        private String salary;
        
        
        @Override
        
    public Object[] toArray() {
            
    return new Object[]{id,name,age,salary,addTime,refreshTime,valid,remark};
        }


        
    public String getAge() {
            
    return age;
        }


        
    public void setAge(String age) {
            
    this.age = age;
        }


        
    public String getSalary() {
            
    return salary;
        }


        
    public void setSalary(String salary) {
            
    this.salary = salary;
        }
    }

    // Tmp類的基類:
    public abstract class BaseDomainObj{
        
    // 領域對象的唯一識別標志
        protected String id;
        
        
    // 名稱
        protected String name;
        
        
    // 對象對應的記錄被添加到數據庫的時間(入庫時間)
        protected String addTime;
        
        
    // 對象對應的記錄最近被更新的時間(更新時間)
        protected String refreshTime;
        
        
    // 備注
        protected String remark;
        
        
    // 記錄是否有效,若為false則不該進入
        protected String valid;
        
        
    /**
         * ?無參構造函數
         
    */
        
    public BaseDomainObj(){
            
        }
        
        
    /**
         * 有參構造函數,使用此函數傳入一個XML,得到相應對象
         * 
    @param xml
         * 
    @throws DocumentException
         
    */
        
    public BaseDomainObj(String xml) throws DocumentException{
            fromXML(xml);
        }
        
        
    /**
         * 將對象轉化為數組形式,便于在表格中顯示
         * 
    @return
         
    */
        
    public abstract Object[] toArray();
        
        
    /**
         * 使用BeanUtils將XML的節點轉化到屬性中
         * 
    @param xml
         * 
    @throws DocumentException
         
    */
        @SuppressWarnings(
    "unchecked")
        
    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.setProperty(
    this,elm.getName(),elm.getText());
                } 
    catch (IllegalAccessException e) {
                    e.printStackTrace();
                } 
    catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

        
    public String getId() {
            
    return id;
        }

        
    public void setId(String id) {
            
    this.id = id;
        }

        
    public String getName() {
            
    return name;
        }

        
    public void setName(String name) {
            
    this.name = name;
        }

        
    public String getAddTime() {
            
    return addTime;
        }

        
    public void setAddTime(String addTime) {
            
    this.addTime = addTime;
        }

        
    public String getRefreshTime() {
            
    return refreshTime;
        }

        
    public void setRefreshTime(String refreshTime) {
            
    this.refreshTime = refreshTime;
        }

        
    public String getRemark() {
            
    return remark;
        }

        
    public void setRemark(String remark) {
            
    this.remark = remark;
        }

        
    public String getValid() {
            
    return valid;
        }

        
    public void setValid(String valid) {
            
    this.valid = valid;
        }
    }

       
    重要的是上面的黑體部分,只要我們保證XML的字段和Tmp對象中的字段是一一對應的,fromXML函數就能保證完成XML到對象的轉換,對于負責具體業務的程序員,在代碼里如下做就可以了:
    String objXML=”;// 從WebService端取出的Tmp對象XML文本
    Tmp tmp=new Tmp(objXML);// 這樣,對象就出來了.
     

    小結:
    一.框架設計者一定要定義好框架的任務,限制具體程序員的行為,否則項目的可讀性可維護性就是一句空話。
    二.框架一定要完成主干的任務的流程,而具體程序員只負責枝節,換言之,具體程序員只該負責簡單的規定好了的任務,如某函數的具體實現。
    三.好的框架完成后,其他人應該能像填空一樣完成任務,要讓他們在完成任務時不需要思考具體的來龍去脈。
    四.好的框架能讓完成任務的程序員盡量平行,減少相互間的交流成本。實際上,框架和工廠流水線的設計某種程度上是相通的。
    五.隨著數據量和規模的增大,一些問題會逐漸顯山露水,這就需要框架設計者有前瞻性的眼光。
    六.如果框架已經不能滿足需求,帶來很多問題時,設計者需要有把前設計推到重來重新組建新框架的勇氣和毅力,當斷不斷,修修補補,蹣跚前行,反受其害。
    posted on 2010-05-19 14:22 何楊 閱讀(1903) 評論(0)  編輯  收藏

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


    網站導航:
     
    主站蜘蛛池模板: 日本系列1页亚洲系列| 亚洲成a人片在线观看日本麻豆| 亚洲成a人片毛片在线| 久久九九全国免费| 亚洲免费网站在线观看| 亚洲精品97久久中文字幕无码| 久久国产免费一区| 久久精品国产亚洲精品2020| 性xxxx视频播放免费| 中国精品一级毛片免费播放| 亚洲国产精品久久人人爱| 四虎永久在线精品免费影视| 精品无码人妻一区二区免费蜜桃 | 免费看男女下面日出水来| 亚洲另类无码专区首页| 精品亚洲永久免费精品| 成在人线AV无码免费| 国产精品亚洲专区无码WEB | 曰批全过程免费视频免费看| 四虎影视永久免费观看网址| 一级毛片在线免费看| 亚洲宅男精品一区在线观看| 中文字幕亚洲日本岛国片| 日韩视频在线免费观看| 青青久久精品国产免费看| 亚洲国产日产无码精品| 亚洲人成伊人成综合网久久久| 好吊妞788免费视频播放| 国产免费一区二区三区不卡| 亚洲日韩av无码中文| 亚洲成人动漫在线观看| 亚洲精品无码久久久久| 亚洲免费福利在线视频| 国产在线观看免费视频软件 | 中国xxxxx高清免费看视频| 午夜不卡AV免费| 亚洲av无码日韩av无码网站冲| 久久久久亚洲AV综合波多野结衣| 最近2019中文免费字幕| 亚洲精品视频免费看| 久久国产精品免费专区|