第四章 Struts框架之構建Controller組件
一、概述
在Struts架構中,Controller主要是ActionServlet,但是對於業務邏輯的操作則主要由Action、ActionMapping、ActionForward這幾個組件協調完成。其中,Action扮演了真正的業務邏輯的實現者,而ActionMapping和ActionForward則指定了不同業務邏輯或流程的運行方向。
應用程序的Controller部分集中於從客戶端接收請求(典型情況下是一個運行瀏覽器的用戶),決定執行什麼商業邏輯功能,然後將產生下一步用戶界麵的責任委派給一個適當的View組件。在Struts中,controller的基本組件是一個ActionServlet類的servlet。這個servlet通過定義一組映射(由Java接口ActionMapping描述)來配置。每個映射定義一個與所請求的URI相匹配的路徑和一個Action類(一個實現Action接口的類)完整的類名,這個類負責執行預期的商業邏輯,然後將控製分派給適當的View組件來創建響應。
Struts也支持使用包含有運行框架所必需的標準屬性之外的附加屬性的ActionMapping類的能力。這允許我們保存特定於我們的應用程序的附加信息,同時仍可利用框架其餘的特性。另外,Struts允許我們定義控製將重定向到的邏輯名,這樣一個行為方法可以請求"主菜單"頁麵,而不需要知道相應的JSP頁麵的實際名字是什麼。這個功能極大地幫助我們分離控製邏輯(下一步做什麼)和顯示邏輯(相應的頁麵的名稱是什麼)。下圖1是Struts的controller組件示意圖:
二、創建Controller組件
Struts包括一個實現映射一個請求URI到一個行為類的主要功能的servlet。因此我們的與Controller有關的主要責任是:
為每一個可能接收的邏輯請求寫一個Action類(也就是,一個Action接口的實現);寫一個定義類名和與每個可能的映射相關的其它信息的ActionMapping類(也就是,一個ActionMapping接口的實現);寫行為映射配置文件(用XML)用來配置controllerservlet。
為應用程序更新web應用程序展開描述符文件(用XML)用來包括必需的Struts組件,我們給應用程序添加適當的Struts組件。
1、Action實現
Action接口定義一個單一的必須由一個Action類實現的方法,就象下麵這樣:
publicActionForwardperform(ActionServletservlet,
ActionMappingmapping,
ActionFormform,
HttpServletRequestrequest,
HttpServletResponseresponse)
throwsIOException,ServletException;
一個Action類的目標是處理這個請求,然後返回一個標識JSP頁麵的ActionForward對象,控製應該重定向這個JSP頁麵以生成相應的響應。Struts架構為應用係統中的每一個Action類隻創建一個實例。因為所有的用戶都使用這一個實例,所以你必須確定你的Action類運行在一個多線程的環境中。下圖2顯示了一個execute()方法如何被訪問:
圖2Action實例的execute()方法
注意,客戶自己繼承的Action子類,必須重寫execute()方法,因為Action類在默認情況下是返回null的。
在Model2設計模式中,一個典型的Action類將在它的perform()方法中實現下麵的邏輯:
驗證用戶session的當前狀態(例如,檢查用戶已經成功地注冊)。如果Action類發現沒有注冊存在,請求應該重定向到顯示用戶名和口令用於注冊的JSP頁麵。應該這樣做是因為用戶可能試圖從"中間"(也就是,從一個書簽)進入我們的應用程序,或者因為session已經超時並且servlet容器創建了一個新的session。如果驗證還沒有發生(由於使用一個實現ValidatingActionForm接口的formbean),驗證這個formbean的屬性是必須的。如果發現一個問題,當作一個請求屬性保存合適的出錯信息關鍵字,然後將控製重定向回輸入表單這樣錯誤可以被糾正。
執行要求的處理來處理這個請求(例如在數據庫裏保存一行)。這可以用嵌入Action類本身的代碼來完成,但是通常應該調用一個商業邏輯bean的一個合適的方法來執行。更新將用來創建下一個用戶界麵頁麵的服務器端對象(典型情況下是request範圍或session範圍beans,定義我們需要在多長時間內保持這些項目可獲得)。返回一個標識生成響應的JSP頁麵的適當的ActionForward對象,基於新近更新的beans。典型情況下,我們將通過接收到的ActionMapping對象(如果我們使用一個局部於與這個映射上的邏輯名)或者在controllerservlet本身(如果我們使用一個全局於應用程序的邏輯名)上調用findForward()得到一個對這樣一個對象的引用。
當為Action類編程時要記住的設計要點包括以下這些:
controllerservlet僅僅創建一個我們的Action類的實例,用於所有的請求。這樣我們需要編寫我們的Action類使其能夠在一個多線程環境中正確運行,就象我們必須安全地編寫一個servlet的service()方法一樣。
幫助線程安全編程的最重要的原則就是在我們的Action類中僅僅使用局部變量而不是實例變量。局部變量創建於一個分配給每個請求線程的棧中,所以沒有必要擔心會共享它們。
盡管不應該,代表我們的係統中Model部分的的beans仍有可能拋出違例。我們應該在我們的perform()方法的邏輯中捕捉所有這樣的違例,並且通過執行以下語句將它們記錄在應用程序的日誌文件中(包括相應的棧跟蹤信息):
servlet.log("Errormessagetext",exception);
作為一個通用的規則,分配很少的資源並在來自同一個用戶(在用戶的session中)的請求間保持它們會導致可伸縮性的問題。另外,我們將會想要防止出現非常大的Action類。最簡單的實現途徑是將我們的功能邏輯嵌入到Action類本身,而不是將其寫在獨立的商業邏輯beans中。除了使Action類難於理解和維護外,這種方法也使得難於重用這些商業邏輯代碼,因為代碼被嵌入到一個組件(Action類)中並被捆綁運行於web應用程序環境中。