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

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

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

    Sung in Blog

               一些技術文章 & 一些生活雜碎
    一、摘要

    建立和維護企業應用程序非常困難。而為這些應用設計出上乘的、易于維護的用戶界面則是所有工作中最讓人畏懼的任務。來自Apache Jakarta 項目的Struts框架為J2EE(Java2平臺企業版)帶來了Model 2 結構。在本文中,兩位作者討論了開發者在使用Struts 的過程中所遇到的問題,以及簡化這些問題的相應方法。

    除非你過去幾年內潛居于石洞之中,否則你不可能沒聽說過Struts framework。Struts是由Apache軟件基金會最初發起的開源,主要是為了促進Web應用演示層內的模型-視圖-控制器(MVC)設計范例。truts 提供了使用Service的MVC模式給Worker 模式。一個設計優秀的結構總是力爭耦合寬松、結合性高。Struts為在多捆綁的企業Web應用的演示層實現這個目標提供了一個機制。 實現企業應用結構所面對的最讓人望而生畏的任務之一就是演示層的創建和維護。用戶期望得到非常功能化的、堅固的、和優雅的灰土用戶界面。因此,演示層的代碼庫使得應用層超負荷運行。另外,不同的顯示平臺如無線電話和PDAs 的出現使得原本復雜的狀況更加復雜的多。

    各種不同的書和文章已經講述了Struts的內部工作原理并且教我們如何使用這個框架。本文詳細闡述了使用Struts 的Web應用開發者所遇到的問題,以及如何解決這些問題。下列方法中有許多可以應用到不同的MVC框架中如即將上市的JavaServer Faces 規范。 Craig R. McClanahan,Struts的創始人之一,造就了這個規范。

    本討論的主題包括:在使用Struts框架,用BEA WebLogic Server建立J2EE(Java2平臺企業版)應用的過程中出現最多問題的所有區域。我們將討論下列專題:
    ·創建/維護 struts-config.xml
    ·表格/會話期管理
    ·Struts 映射和用戶界面的關系
    ·管理Back按鈕
    ·用戶認證
    ·用戶界面控制流程
    · 異常處理
    ·測試

    二、挑兩個,早上呼我

    Struts框架毫無疑問,減輕了企業應用程序的用戶界面的開發和維護。但是,即使只是在一個簡單的應用中使用了Struts ,開發者也會迅速的認識到struts-config.xml這個惡魔。 這個文件很有可能迅速變得難于處理。在建立企業應用時,struts-config.xml 能夠多出 500個動作映射,使得自身變得真正地難于管理。

    我們推薦兩個工具來幫助治理這個頭疼的問題。首先,使用來自Alien-Factory 的Microsoft Visio 和StrutsGUI 文檔化你的用戶界面流程。StrutsGUI是一個Visio 模版,它對使用Struts 術語描述用戶流程圖有幫助。在Struts 模版內有一個隱藏的功能:只要右鍵點擊該項,選擇Edit Title Properties,然后選擇Tools項,你就能夠在該圖的基礎上生成struts-config.xml 文件。例如,圖1中顯示的簡單應用生成了如下列代碼所示的struts-config.xml :



    Figure 1. StrutsGUI model. Click on thumbnail to view full-size image.


    <?xml version="1.0" encoding="ISO-8859-1" ?>
    
    <!-- Struts Config XML - Sample Struts App -->
    <!-- ===================================== -->
    
    <!-- AutoGenerated from : c:\dev\javaworld\app\sample.vsd -->
    <!-- AutoGenerated on   : 02-18-2003 23:05:47 -->
    <!-- AutoGenerated by   : Struts GUI v2.11   (c)2002 Alien-Factory -->
    <!--                    : See 'http://www.alien-factory.co.uk' for details -->
    
    <!-- GET YOUR STICKY FINGERS OFF! i.e. Do not edit. -->
    
    <!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
    
    <struts-config>
    
      <!-- ====================== Form Bean Definitions =================== -->
      <form-beans>
      </form-beans>
    
      <!-- ================= Global Forward Definitions =================== -->
      <global-forwards>
      </global-forwards>
    
      <!-- ======================= Action Definitions ===================== -->
      <action-mappings>
        <action  path="/Login"
                 type="com.agilquest.onboard.presentation.actions.LoginAction">
          <description>Authenticates and authorizes a user.</description>
        </action>
      </action-mappings>
      
    </struts-config>


    為了使你的用戶界面流程圖更加復雜,我們推薦在使用StrutsGUI 時增加一個步驟。在你的StrutsGUI Visio 文檔內,你可以輕易的將每個JSP (JavaServer Pages)頁鏈接到它在應用中的實際屏幕快照。創建這個界面不但確實有助于應用程序的文檔化,更重要的是,它成為了培訓新的從事用戶界面設計的開發者的一件極好的工具。

    另一個幫助管理Struts應用的工具就是由James Holmes 發明的Struts Console。本質上,這個工具提供了一組設備,這些設備使你能夠得到與StrutsGUI 相同的終點,但是它們在途徑和長度上有區別。這兩個工具都執行良好,其中任何一個都可以增強基于Struts的企業應用的可維護性。

    三、現在,我將表格放在哪里?

    ActionForm 會話期管理有點棘手。ActionForm的周期怎樣?它在請求周期內或者會話期周期內嗎?為了得到它所代表的功能周期,方案之一就是將ActionForm置于會話期之內。在這種情況下,你通常怎樣維護這些ActionForm 對象呢?誰知道不再需要他們時刪除他們要承擔什么責任呢?典型的情況是:用戶通過菜單從一個功能轉為使用另一個功能。在這種情況下,原來的ActionForm 對象就應該從會話期刪除,并且創建新的ActionForm 對象。這時還應該出現一個集中的Action類,MenuAction,它只處理菜單切換。這個Action 類從會話期刪除多余的ActionForm 對象。然后將用戶前進到創建新ActionForm 對象所在的新頁面。

    在這種情況下,站在用戶的立場上或者基于用戶權限,我們應該如何顯示不同的菜單項呢?這個菜單也應該國際化,并且它修改時應該以用戶權限為基礎;也就是說,如果許可修改了,菜單也要相應的修改。有一種方法可以持續用戶的權限。當用戶注冊時, MenuFactory根據這些權限創建菜單,為了增加安全性, MenuAction類在允許用戶進行到他所選擇的功能之前需要認證用戶。命名struts-config.xml 中的ActionForm 對象的首要規則是對象名以Form 結束,從而簡化了會話期內這些表格的維護。例如: ReservationForm, SearchUserForm, BankAccountForm, UserProfileForm,等等。

    下列代碼描述了一個具有Action 映射的普通菜單切換動作,它進一步闡明了ActionForm(s)管理:

    public class MenuAction {
    
      public ActionForward perform(ActionMapping       _mapping,
                                   ActionForm          _form,
                                   HttpServletRequest  _request,
                                   HttpServletResponse _response)
                                      throws IOException, ServletException {
    
        // Check end-user permissions whether allowed into the requested     
        // functionality 
        checkIfUserAllowedToProceed(_mapping, _form, _request, _response); 
    
        // Clean up the session object (this logic is in its own method)
        String formName = null; 
    
        HttpSession session = _request.getSession();
        Enumeration e = session.getAttributeNames();  
    
        while(e.hasMoreElements()) {
         
          formName = (String)e.nextElement();
    
          if (formName.endsWith("Form")){
            session.removeAttribute(formName);
          }    
        }
    
        // Now find out which functionality the end-user wants to go to
        String forwardStr = _request.getParameter("nextFunctionality");
    
        if (forwardStr != null && forwardStr.trim().length() > 0){
          return _mapping.findForward(forwardStr);
        }
        else {
          return _mapping.findForward("index");
        }
      }  
    }


    下列Action映射就是一個闡述如何以菜單選擇為基礎實現動作的例子:

    <!-- A generic menu action that forwards the user from one 
         functionality to another functionality (after checking permissions)
    -->
    <action path="/menuAction"
            type="x.y.z.MenuAction"
            input="/menu.jsp">      
      <forward name="create_reservation" path="/actionResv.do"/> 
      <forward name="index"              path="/menu.jsp"/> 
      <forward name="add_person"         path="/actionPerson.do"/> 
      <forward name="logout"             path="/actionLogout.do"/> 
    </action>


    例子和映射都是可以自我解釋的。

    四、再說一次,我們是怎樣關聯的?

    任何JSP頁的許多輸入點與許多現有點之間都有一個關系,這取決于頁面本身的復雜程度。認識到這些關系對于理解和維護用戶界面是至關重要的。我們已經將JSP頁和Action 類之間的關系定義為:
    ·1:1 關系
    ·1:N 關系
    ·N:N關系

    1:1 關系 在1:1 關系中,用戶通過Action類從一個JSP頁切換到另一個頁面;這就使得JSP頁和Action 之間容易形成一個緊密的耦合。唯一的額外開銷就是struts-config.xml 中有一個Action映射。這個在struts-config.xml 中只有一個Action 映射的簡單Action 可用于從一個頁面切換到另一個頁面。直接通過一個JSP頁訪問另一個JSP頁是不太現實的;他不能夠檢查轉向目標JSP頁的用戶權限(如果可行的話)。他還導致了維護方面的問題。為了避免這些問題,可以總是通過Action 類從一個JSP頁轉向另一個JSP頁:

    <!-- A generic action that forwards request from one JSP page to another JSP page -->
    <action path="/forwardAction"
            type="x.y.z.One2OneAction"
            input="/test1.jsp">      
      <forward name="continue"  path="/test2.jsp"/> 
    </action>


    1:N關系 稍微復雜一點的關系就是JSP頁有多個現有點但是只有一個輸入點,也叫做1:N關系。在這種情況下,總是使用一個單一 Action類分支到不同的目標。這就保證了Action 在將用戶推進到目標之前能夠檢查不同的情況或者權限。唯一的額外開銷就是在struts-config.xml 中有一個Action 映射。這也推動了JSP頁與Action 映射之間的1:1 映射。下面的Action映射標出了一個映射,它有一個單一輸入點和多個前推,多個前推代表著多個現有點:

    <!-- A generic action that forwards request from one JSP page to different 
         branches depending on the selected hyperlink, by the end-user
    -->
    <action path="/branchAction"
             type="x.y.z.One2NAction"
             input="/test1.jsp">      
      <forward name="target1"   path="/test2.jps"/> 
      <forward name="target2"   path="/test3.jsp"/> 
      <forward name="target3"   path="/someAction.do"/> 
    </action>


    N:N 關系 最復雜的關系,即N:N關系,指的是JSP頁或者Action 類有多個輸入點和多個現有點。N:N 關系是頻繁出現在企業應用程序中的一個有趣和復雜的部分。N:N關系最初應用在不同的JSP頁訪問一個公共JSP頁或者一個公共Action類的情況下。假設用戶進入的JSP頁是一個網絡中心(特別是這個JSP頁由不同的JSP頁都可到達),但是用戶又想返回或者取消這個流程;那么開發者就處于進退兩難的局面:不知道怎樣將用戶送往正確的頁面。

    另一個場景是:Action類接合到數據庫(通過不同的功能函數或者JSP頁),并且出現錯誤。以用戶原來所在的位置為基礎,將用戶發送回原來所在的位置或者適當前推,這需要仔細推敲。struts-config.xml映射證明是沒有幫助的,因為輸入域是一個確定的JSP頁或者 Action 類。我們創建的結構應該足夠靈活,這樣開發者不必在struts-config.xml 折騰就能夠輕而易舉的修改流程邏輯。這就是N:N關系所要解決的問題。通過實現一個能夠靈活發送用戶到目標所在地或者目的文件的界面,這個值修改起來就比較容易。下面的Action映射給出的映射有多個輸入點和多個前推,這些前推代表多個現有點:

    public class N2NAction {
    
      public ActionForward perform(ActionMapping      _mapping,
                                   ActionForm         _form,
                                   HttpServletRequest _request,
                                   HttpServletResponse _response)
                                      throws IOException, ServletException {
    
        N2NInterface if = (N2NInterface)_form;
    
        //Execute some business functionality here
        try{
          //Business logic successful?
                   
        }
        catch(Exception e){
    
          //Indicates failure
          return _mapping.findForward(if.getSource()); 
        }
    
        //Indicates success
        return _mapping.findForward(if.getDestination());            
    
      } 
    
    }
    
    <!-- A generic action that forwards request from one JSP page to another JSP page -->
    <action  path="/sourceAndDestinationAction"
             type="x.y.z.N2NAction"
             input="/test1.JSP">      
      <forward name="source1"          path="/source1.JSP"/> 
      <forward name="source2"          path="/source2.JSP"/> 
      <forward name="source3"          path="/someAction.do"/> 
      <forward name="destination1"     path="/destination1.JSP"/> 
      <forward name="destination2"     path="/destination1.JSP"/> 
      <forward name="destination3"     path="/destination2.JSP"/>
    </action>
    
       A hyerplink can be something like
       <a href="sourceAndDestinationAction.do?
        source=source1&destination=destination1">click me</a>
       <a href="sourceAndDestinationAction.do?
        source=source2&destination=destination2">click me too</a>



    所有的ActionForms 默認地都必須所有這三種關系(通常通過界面)。使用普通的Action 類,你可以自由地在用戶界面流程中移動.

    .

    目錄:
    五、Back之痛!
    六、你是誰,在這做什么?
    七、我們去往何處?
    八、犯錯乃人之常情...我們不是圣賢
    九、測試,就是用戶想要的!
    十、簡化Struts 開發


    五、Back之痛!

    設計演示層,有一個好方法就是按照功能設計。例如:在一個預訂系統中預訂的時候,將所有的相關動作類封裝到一個包裝內如

    com.companyname.productname.presentation.
       reservation.mak


    就是一個不錯的方法。深度封裝也可以應用到ActionForm類中。表格應該處在功能周期的會話期內。這就保證了該完整功能所需的數據放在表格對象本身之內。這樣用戶就能夠前進到功能的任何頁面并且找到該頁上顯示的正確數據。接著用戶還能夠在最終保存該數據之前更新數值。因此,就會產生一個有趣的兩難局面:當用戶確認數據、按下瀏覽器中的Back 按鈕、修改并再次提交的話會發生什么事情?例如,在創建了數據庫中的預定后,用戶返回并試圖再次提交同樣的數據。演示層必須在應用層有機會抱怨之前先捕捉到這個錯誤。處理這種情況的方法之一就是在提交之前創建一個令牌,提交之后檢查令牌的有效性,并立即修改令牌值——這樣用戶就不能夠再次使用Back 按鈕來提交同樣的值。

    這個方法的弊病之一就是需要管理令牌:例如,如果用戶試圖保存數據失敗了,但是令牌值卻已經修改了。如果這樣的話,用戶就不能夠修改數據,和在不重新設置令牌的情況下重新提交。相應的Action 類也不會允許提交。那么到底什么時候才應該重置令牌呢?用戶可以遍歷六個頁面,那么在確認它的修改時就會收到錯誤,然后定向到六個頁面中的任何一個頁面。

    要管理這個問題,就需要在用戶請求一個特別功能時創建一個表格并在會話期內存儲一個默認的令牌值。在保存之前,用戶可以多次使用Back按鈕進行修改。一旦用戶提交了這些修改,只有在保存成功之后才能重新設置令牌。如果確認失敗,用戶可以使用Back 按鈕或者前進到產生失敗的頁面,修改數據然后重新提交數據。一旦確認成功的話,令牌值就會修改。ActionForm 對象本身也可以包含令牌值(令牌值可以通過程序設置)。還有一個方法就是,在第一次提交之后,Submit 按鈕就變得無效、不允許用戶再次提交直到有事情發生。我們建議每個ActionForm 處理他自己的令牌,應用至多是使用不止一個不同的ActionForm對象來處理一個特殊功能。

    要了解另外一個復制表格提交的方法,請參考Romain Guay的 (JavaWorld, March 2003).

    六、你是誰,在這做什么?

    企業應用必須設計成能夠同時支持多個認證模式。這對于來自ISVs(獨立的軟件開發商)的軟件更為重要。例如,假設一個應用的認證要求是單一登錄、詢問用戶密碼、條形碼認證或者指紋認證,甚至是將來有可能出現的聲音認證。JAAS (Java 認證和授權服務),這個可插入的認證機制,證明可用于這個方面。默認的, Struts 1.1 支持JAAS。JAAS也可用在具有極好結果的Struts 1.0中。對于認證,一個很好的繼承性設計將會推動使用不同的認證動作類,每個動作類都有他自己唯一的Action 映射。這種設計方法將會推動對不同認證的同時支持。如果需要添加新的認證機制,只要用一個支持該認證的Action類在struts-config.xml內創建一個新的動作映射即可。

    七、我們去往何處?

    流程控制器的設計目的是限制和引導用戶通過應用程序,它在保證用戶遵循特殊流程方面很實用。讓流程控制器以系統的地位為基礎證明是有用的。無論用戶什么時候試圖訪問系統中的功能,流程控制器都可以確保用戶有權進入到特殊流程中。而且,就算出現了意外情況,用戶也能夠被引導到恰當的頁面。放置流程控制器最好的地方是在超類中。只有在流程控制器滿意之后用戶才允許執行特定動作。

    圖 2 給出了一個簡單的繼承性模型,它可用來使用合并到用戶認證中的流程控制器。



    Figure 2. Simple authentication class model



    八、犯錯乃人之常情...我們不是圣賢

    Struts框架通過ActionError和ActionErrors類為我們提供了異常處理結構。 ActionError就是——存在于Action 或者ActionForm類中的錯誤,或者應用層給出的錯誤。ActionError 類典型地可通過使用簡單的鑰匙構造,或者通過使用鑰匙/值對來構造。如果你的應用有國際化關系,那么該鑰匙可用于查找國際化信息資源數據庫。如果這些國際化關系不存在的話,那么取代值可用于顯示錯誤信息。至多有四個占位符設置在對象排列中,每個占位符都包含整個錯誤信息的一個單獨部分。這些值可構造得與MessageFormatter 類相似。

    ActionErrors類,擴展了ActionMessage 類,它是具有單一公共方法的ActionError 類的集合,這個單一公共方法是add(java.lang.String property, ActionError error)。這個方法簽名中的第二個參數是直接的:他指的是已經存在的真實錯誤(ActionError)。通過將錯誤信息與指定的域關聯起來,屬性參數可用于域級別的校驗。既然錯誤信息與指定域關聯,你可以輕易地將信息定位于有疑問的域附近或者定位于它能產生最大意義的任何地方。例如,如果你在用戶的邏輯標記符之上進行域級別的校驗,并且在類LoginAction 中出現錯誤,你可以使用下列代碼片斷:

    ActionErrors errors = new ActionErrors();
    errors.add("logiinID", new ActionError("loginID.invalid"));
    saveErrors(_request, errors);
    return (new ActionForward(_mapping.getInput()));


    字符串"loginID.invalid"就是用于信息資源數據庫中國際化字符串值的鑰匙。為了顯示錯誤信息,在你的login.jsp中,使用下列HTML:

    <font color="red"><html:errors property="loginID"/></font>


    有了對用于異常和異常鏈的Struts 框架設備的這個特別理解,要管理整個企業應用結構中的錯誤需要一個更加通用的方法。企業應用異常框架中最重要的宗旨有:
    ·開發異常類體系
    ·從系統異常中退耦用戶異常
    ·給出異常的內容、類型和嚴重級別
    ·退耦異常處理和應用日志
    ·創建用于異常鏈的工具
    ·具體化用于國際化用途的異常字符串

    許多文章和關于這個問題的完整書籍,都討論了使用Java的異常處理。Brian Goetz,在他的JavaWorld 系列 "Exceptional Practices" 中提供了關于這個主題的非常好的建議。

    九、測試,就是用戶想要的!

    盡管許多開發者都把測試看作是軟件開發的最不吸引人的部分,測試企業應用對于它的整體成功還是很關鍵的。許多靈活的方法,如極限程序設計(XP),將測試放在企業應用開發的最前部。這種強調實際上是相當爽的。為了簡潔起便,我們只討論用于Struts 類的單元和集成測試策略。

    兩個原始策略可用于服務器端的測試:模擬對象(MO)測試和容器內(IC)測試。模擬對象測試從本質上擴展了"多樹樁的"行為,并且它是自我說明的。從本質上說,開發者負責模擬定義接口的類,這些接口歸進行測試的類使用。使用這個方法,開發者可以模擬容器。模擬對象測試也被叫做endotesting ,因為設置測試的動作是在受限環境中的類的內部進行的。

    容器內測試這個策略應用于下列情況:在產品環境中將要使用的實際容器也用于整個單元測試過程中。這些測試案例必須至少合并到使用Ant的每夜建立過程中。單元測試Java類的有效方法之一就是使用JUnit。他是用來測試普通JavaBeans 的一件極好的工具。另外一個重要的開源工具就是Cactus。Cactus,JUnit框架的擴展,可以在單元級別上測試服務器端的代碼。Cactus 支持用于服務器端測試的容器內方法。這兩個測試工具都是很有用的,它是任何追求品質的企業應用開發團隊的基石。

    對于測試Struts來說,普遍接受的策略就是通過應用層測試演示層。你選擇模擬對象測試方法還是容器內測試方法,主要取決于你們團隊的需要和安慰級別。要自動執行Struts 測試,需要創建一個名為StrutsTestCase 的、對于JUnit測試方法的擴展,它對MO 和 IC都支持。使用StrutsTestCase 測試Action 和ActionForm 類,是整個測試策略的一部分,它應該包括這里討論的回歸測試工具。其他工具如Apache JMeter 提供了應力/負載測試。

    十、簡化Struts 開發

    總的來說,我們討論了你應該如何簡化struts-config.xml文件的設計和維護,定義了錯誤處理和測試策略,簡化了常見問題如處理Web瀏覽器的Back按鈕,基于會話期的表格管理以及用戶認證。最后,我們還清楚的文檔化了用戶界面控制流程的管理。我們的經歷告訴我們:開發高品質的企業應用是困難的。當你使用Struts框架創建企業應用時,融合我們的建議到你的開發計劃中,你就能夠使你的設計、開發和維護費用減至最低。

    關于作者 Michael Coen 是AgilQuest Corporation的軟件結構師。在AgilQuest時, Mike成為了OnBoard的基于J2EE的企業軟件應用程序開發團隊中的一員。Mike 從事專業軟件開發已有11年多。

    Amarnath Nanduri 是AgilQuest Corporation的資深軟件開發工程師。 Amar在過去六年內一直從事企業軟件應用程序的設計和開發,特別是在用戶界面設計和開發方面。Amar 使用Struts框架進行開發已有兩年。

    posted on 2005-10-24 22:13 Sung 閱讀(225) 評論(0)  編輯  收藏 所屬分類: Struts
    主站蜘蛛池模板: 亚洲最新在线视频| 日韩va亚洲va欧洲va国产| 亚洲AV无码一区二区三区在线| 久9这里精品免费视频| 亚洲第一中文字幕| 免费精品99久久国产综合精品| 亚洲爆乳精品无码一区二区三区| av网站免费线看| 国产亚洲精品资源在线26u| 免费观看久久精彩视频| 亚洲综合在线成人一区| 国产精品69白浆在线观看免费| 亚洲一区二区三区在线网站| 久久WWW免费人成人片| 精品亚洲国产成人av| 亚洲伊人成无码综合网| 91视频免费网站| 亚洲无成人网77777| 在线观看免费宅男视频| 一二三四在线观看免费中文在线观看| 国产成人综合亚洲AV第一页| 99热在线免费播放| 亚洲一区二区三区写真| 亚洲成av人片在线观看天堂无码| a级毛片在线免费| 亚洲色欲色欲www| 一本色道久久88综合亚洲精品高清| 久久久久久久久久免免费精品 | 美腿丝袜亚洲综合| 久久免费的精品国产V∧| 亚洲AV无码无限在线观看不卡 | 久久精品国产亚洲AV忘忧草18 | 亚洲区小说区激情区图片区| 最近的中文字幕大全免费8| 亚洲AV无码国产精品永久一区| 中文亚洲AV片在线观看不卡| 99爱在线精品免费观看| 一级日本高清视频免费观看| 亚洲小说图片视频| 亚洲精品制服丝袜四区| 成人毛片免费播放|