Struts 用戶指南(二)

- 中國WEB開發者網絡 (http://www.webasp.net)
-- 技術教程 (http://www.webasp.net/article/)
--- Struts 用戶指南(二) (http://www.webasp.net/article/19/18070.htm)
-- 作者:未知
-- 發佈日期: 2005-04-28
Sunday, January 5 2003 6:02 PM 
2. 創建Model組件
2.1 概述
你用到的應用程序的需求文檔很可能集中於創建用戶界面。然而你應該保證每個提交的請求所需要的處理也要被清楚的定義。通常說來,Model 組件的開發者集中於創建支持所有功能需求的JavaBeans類。一個特殊應用要求的beans的精確特性依賴於具體需求變化會非常的大,但是它們通常可以分成下面討論的幾種類型。然而,首先對「範圍」概念做一個簡短的回顧是有用的,因為它與beans有關。 




2.2 JavaBeans和範圍
在一個基於web的應用程序中,JavaBeans可以被保存在(並從中訪問)一些不同「屬性」的集合中。每一個集合都有集合生存期和所保存的beans可見度的不同的規則。總的說來,定義生存期和可見度的這些規則被叫做這些beans的範圍。JSP規範中使用以下術語定義可選的範圍(在圓括號中定義servlet API中的等價物): 


page - 在一個單獨的JSP頁面中可見的Beans,生存期限於當前請求。(service()方法中的局部變量)

request - 在一個單獨的JSP頁面中可見的Beans,也包括所有包含於這個頁面或從這個頁面重定向到的頁面或servlet。(Request屬性) 

session - 參與一個特定的用戶session的所有的JSP和servlet都可見的Beans,跨越一個或多個請求。(Session屬性) 

application - 一個web應用程序的所有JSP頁面和servlet都可見的Beans。(Servlet context屬性)

記住同一個web應用程序的JSP頁面和servlets共享同樣一組bean集合是很重要的。例如,一個bean作為一個request屬性保存在一個servlet中,就像這樣: 
MyCart mycart = new MyCart(...);
request.setAttribute("cart", mycart);


將立即被這個servlet重定向到的一個JSP頁面使用一個標準的行為標記看到,就像這樣: 
<jsp:useBean id="cart" scope="request"
class="com.mycompany.MyApp.MyCart"/>


2.3 ActionForm Beans
Struts框架通常假定你已經為每一個你的應用程序中請求的輸入創建了一個 ActionForm bean(即一個實現了ActionForm 接口的類)。如果你在你的 ActionMapping 配置文件中定義了這樣的beans(見「創建Controller組件」),Struts的controller servlet在調用適當的 Action 方法前將自動為你執行如下的服務:
用適當的關鍵字檢查用戶的session中是否有適當的類的bean的一個實例。 

如果沒有這樣的session範圍的bean,自動建立一個新的bean並添加到用戶的session中。 

對每個名字對應於bean中的一個屬性的請求參數,調用相應的set方法。這個操作類似於當你以通配符「*」選擇所有屬性使用標準的JSP行為標記 <jsp:setProperty> 。 

更新的ActionForm bean在被調用時將被傳遞給Acton類的perform()方法,以使這些值能夠立即生效。 

當你在寫你的ActionForm beans時,記住以下的原則: 

ActionForm 接口本身不需要特殊的實現方法。它是用來標識這些特定的beans在整個體系結構中的作用。典型情況下,一個ActionForm bean只包括屬性的get方法和set方法,沒有商業邏輯。 

通常在一個ActionForm bean中只有很少的輸入驗證邏輯。這樣的beans存在的主要理由是保存用戶為相關的表單所輸入的大部分近期值 -- 甚至在錯誤被檢測到時 -- 這樣同樣的頁面可以被重建,伴隨有一組出錯信息,這樣用戶僅僅需要糾正錯誤的字段。用戶輸入的驗證應該在 Action 類中執行(如果是很簡單的話),或者在適當的商業邏輯beans中執行。 

為每個表單中出現的字段定義一個屬性(用相關的getXxx()和setXxx()方法)。字段名和屬性名必須按照JavaBeans的約定相匹配。例如,一個名為username 的輸入字段將引起 setUsername() 方法被調用。 

你應該注意一個「表單」在這裡討論時的意義並不必須對應於用戶界面中的一個單獨的JSP頁面。在很多應用程序中一個「表單」(從用戶的觀點)延伸至多個頁面也是很平常的。想想看,例如,通常在安裝新的應用程序時使用的導航安裝程序的用戶界面。Struts鼓勵你定義一個包含所有字段屬性的單獨的ActionForm bean。不管字段實際上是顯示在哪個頁面上。同樣的,同一表單的不同的頁面應該提交到相同的Action類。如果你遵照這個建議,在大多數情況下,頁面設計者可以重新組織不同頁面中的字段而不需要改變處理邏輯。 
2.4 系統狀態Beans
系統的實際狀態通常表示為一組一個或多個的JavaBeans類,其屬性定義當前狀態。例如,一個購物車系統包括一個表示購物車的bean,這個bean為每個單獨的購物者維護,這個bean中包括(在其它事物之中)一組購物者當前選擇購買的項目。分別地,系統也包括保存用戶信息(包括他們的信用卡和送貨地址)、可獲得項目的目錄和它們當前庫存水平的不同的beans。 

對於小規模的系統,或者對於不需要長時間保存的狀態信息,一組系統狀態beans可以包含所有系統曾經經歷的特定細節的信息。或者經常是,系統狀態beans表示永久保存在一些外部數據庫中的信息(例如CustomerBean對像對應於表CUSTOMERS 中的特定的一行),在需要時從服務器的內存中創建或清除。在大規模應用程序中,Entity EJBs也用於這種用途。 


2.5 商業邏輯Beans
你應該把你的應用程序中的功能邏輯封裝成對為此目的設計的JavaBeans的方法調用。這些方法可以是用於系統狀態beans的相同的類的一部分,或者可以是在專門執行商業邏輯的獨立的類中。在後一種情況下,你通常需要將系統狀態beans傳遞給這些方法作為參數處理。 

為了代碼最大的可重用性,商業邏輯beans應該被設計和實現為它們不知道自己被執行於web應用環境中。如果你發現在你的bean中你必須import一個javax.servlet.* 類,你就把這個商業邏輯捆綁在了web應用環境中。考慮重新組織事物使你的 Action 類(Controller任務的一部分,在下面描述)翻譯所有從HTTP請求中請求被處理為對你的商業邏輯beans屬性set方法調用的信息,然後可以發出一個對execute() 的調用。這樣的一個商業邏輯類可以被重用在除它們最初被構造的web應用程序以外的環境中。 

依賴於你的應用程序的複雜度和範圍,商業邏輯beans可以是與作為參數傳遞的系統狀態beans交互作用的普通的JavaBeans,或者使用JDBC調用訪問數據庫的普通的JavaBeans。而對於較大的應用程序,這些beans經常是有狀態或無狀態的EJBs。 

2.6 題外話: 訪問關係數據庫
很多web應用程序利用一個關係數據庫(通過一個JDBC driver訪問)來保存應用程序相關的永久數據。其它應用程序則使用Entity EJBs來實現這個目的,他們委派EJBs自己來決定怎樣維護永久狀態。如果你是使用EJBs來實現這個目的,遵照EJB規範中描述的客戶端設計模式。 

對於基於直接數據庫訪問的web應用程序,一個普通的設計問題是當需要訪問低層數據庫時怎樣產生一個適當的JDBC連接對象。解決這個問題有幾種方法 -- 以下原則描述了推薦的一種方法: 

創建或得到一個允許一組數據庫連接被多個用戶共享的ConnectionPool類。Struts(當前)沒有包括這樣的一個類,但是有很多這樣的類可以得到。 

當應用程序初始化時,在應用程序展開(deployment)描述符中定義一個有一個「啟動時加載」值的servlet。我們將把這個servlet叫做啟動servlet。在大多數情況下,這個servlet不需要處理任何的請求,所以沒有一個 <servlet-mapping> 會指向它。 


在啟動servlet的 init() 方法中,配置並初始化一個ConnectionPool類的實例,將其保存為一個servlet context屬性(從JSP的觀點看等同於一個application範圍的bean)。通常基於傳遞給啟動servlet初始化參數來配置聯接緩衝池是很方便的。 


在啟動servlet的 destroy() 方法中,包含了釋放聯接緩衝池所打開的聯接的邏輯。這個方法將在servlet容器結束這個應用程序的時候被調用。 


當 Action 類需要調用一個需要數據庫聯接的商業邏輯bean中的方法(例如「insert a new customer」)時,將執行下面的步驟: 


為這個web應用程序從servelt context屬性中得到一個聯接緩衝池對象。 

調用聯接緩衝池對象的 open() 方法來得到一個在Action 類調用中使用的聯接。 

調用商業邏輯bean中合適的方法,將數據庫聯接對像作為一個參數傳遞給它。 

調用分配的聯接中的 close() 方法,這將引起這個聯接為了以後其它請求的重用被返回到緩衝池中。 

一個通常的編程錯誤是忘記了把數據庫聯接返回給緩衝池,這將最終導致用完所有的聯接。一定要確信 Action 類的邏輯總是返回聯接,甚至在商業邏輯bean拋出一個違例時。 


遵照上面推薦的設計模式意味著你能夠編寫你的商業邏輯類而不需要擔心它們怎樣得到一個JDBC聯接來使用-- 簡單地在任何需要訪問數據庫的方法中包含一個Connection參數。當你的商業邏輯類在一個web應用程序中被利用時,分配和釋放適當的聯接是 Action 類的責任。當你使用相同的商業邏輯類時,例如,在一個批處理工作中,提供一個適當的聯接是那個應用程序的責任(這不需要從緩衝池中獲得,因為大多數批處理工作運行於一個單線程環境中)。 

webasp.net