Struts1.1 介紹

前一段時間好象有人問關於Struts的事情,本人留意了一下.請有興趣的同志不防研究研究.
(1)
1. 介紹

1.1 Model-View-Controller (MVC) 設計模式

FIXME - 需要一個對該模式一般性的介紹。(譯註:可以參考機械工業出版社的《設計模式》。)
 
1.2 將MVC概念映射到Struts組件中

Struts 的體系結構實現了Model-View-Controller設計模式的概念,它將這些概念映射到web應用程序的組件和概念中。 
這一體系結構中每個主要的組件都將在下面做詳細的討論。 

1.3 Model: 系統狀態和商業邏輯JavaBeans

基於MVC的系統中的 Model 部分可以細分爲兩個概念 -- 系統的內部狀態, 能夠改變狀態的行爲。用語法術語來說,我們可以把狀態信息當作名詞(事物),把行爲當作動詞(事物狀態的改變)。 
通常說來,你的應用程序將系統內部的狀態表示爲一組一個或多個的JavaBeans,使用屬性(properties)來表示狀態的細節。依賴於你的應用程序的複雜度,這些beans可以是自包含的(以某種方式知道怎樣永久地保存它們的狀態信息),或者可以是正面的(facades),知道當被請求時怎樣從外部數據源(例如數據庫)中取得信息。Entity EJBs通常也用來表示內部狀態。
大型應用程序經常將系統可能的商業邏輯行爲表示爲可以被維護狀態信息的beans調用的方法。舉個例子,你有一個爲每個當前用戶保存在session中的購物車bean,裏面是表示當前用戶決定購買物品的屬性。這個bean有一個checkOut()方法用來驗證用戶的信用卡,將定單發給庫房以選擇貨品和出貨。別的系統分別地表示同樣的行爲,或許使用Session EJBs。
在一些小型應用程序中,同樣的行爲又可能嵌入到作爲Controller一部分的 Action 類中。這在邏輯非常簡單或者並不想要在其它環境中重用這些商業邏輯時是恰當的。Struts框架支持所有這些方法,但建議將商業邏輯(“做什麼”)和 Action 類(“決定做什麼”)分離開。 

1.4 View: JSP頁面和表示組件

基於Struts的應用程序中的 View 部分通常使用JSP技術來構建。JSP頁面包含稱爲“模版文本”的靜態HTML(或XML)文本,加上插入的基於對特殊行爲標記解釋的動態內容。JSP環境包括了其用途由JSP規範來描述的一套標準的行爲標記,例如 <jsp:useBean> 。另外,還有一個用來定義你自己標記的標準機制,這些自定義的標記組織在“定製標記庫”中。 
Struts包括了一個廣闊的便於創建用戶界面,並且充分國際化的定製標記庫,與作爲系統 Model 部分一部分的ActionForm beans美妙地相互配合。這些標記的使用將在後面做詳細討論。
除了JSP頁面和其包含的行爲及定製標記,商業對象經常需要能夠基於它們在被請求時的當前狀態將自己處理成HTML(或XML)。從這些對象處理過的輸出可以很容易地使用 <jsp:include> 標準行爲標記包括在結果的JSP頁面中。 

1.5 Controller: ActionServlet和ActionMapping

應用程序的 Controller 部分集中於從客戶端接收請求(典型情況下是一個運行瀏覽器的用戶),決定執行什麼商業邏輯功能,然後將產生下一步用戶界面的責任委派給一個適當的View組件。在Struts中,controller的基本組件是一個 ActionServlet 類的servlet。這個servlet通過定義一組映射(由Java接口 ActionMapping 描述)來配置。每個映射定義一個與所請求的URI相匹配的路徑和一個 Action 類(一個實現 Action 接口的類)完整的類名,這個類負責執行預期的商業邏輯,然後將控制分派給適當的View組件來創建響應。 
Struts也支持使用包含有運行框架所必需的標準屬性之外的附加屬性的 ActionMapping 類的能力。這允許你保存特定於你的應用程序的附加信息,同時仍可利用框架其餘的特性。另外,Struts允許你定義控制將重定向到的邏輯名,這樣一個行爲方法可以請求“主菜單”頁面(舉例),而不需要知道相應的JSP頁面的實際名字是什麼。這個功能極大地幫助你分離控制邏輯(下一步做什麼)和顯示邏輯(相應的頁面的名稱是什麼)。 

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 類的責任。當你使用相同的商業邏輯類時,例如,在一個批處理工作中,提供一個適當的聯接是那個應用程序的責任(這不需要從緩衝池中獲得,因爲大多數批處理工作運行於一個單線程環境中)。 

3. 創建View組件

3.1 概述

這一章集中於創建應用程序中的 View 組件的任務,主要使用JSP技術建立。特別的,Struts除了提供了與輸入表單的交互外還提供了建立國際化應用程序的支持。幾個其它的與View相關的主題也被簡單地討論。 

3.2 國際化消息

幾年之前,應用程序開發者能夠考慮到僅僅支持他們本國的只使用一種語言(或者有時候是兩種)和通常只有一種數量表現方式(例如日期、數字、貨幣值)的居民。然而,基於web技術的應用程序的爆炸性增長,以及將這些應用程序展開在Internet或其它被廣泛訪問的網絡之上,已經在很多情況下使得國家的邊界淡化到不可見。這種情況轉變成爲一種對於應用程序支持國際化(經常被稱做“i18n”,因爲18是字母“i”和字母“n”之間的字母個數)和本地化的需求。 
Struts建立於Java平臺之上爲建立國際化和本地化的應用程序提供幫助。需要熟悉的關鍵概念是: 
Locale - 基礎的支持國際化的Java類是 java.util.Locale 。每個 Locale 代表一個特別的國家和語言選擇(加上一個可選的語言變量),以及一套格式假定,例如數字和日期等等。 
ResourceBundle - java.util.ResourceBundle 類提供支持多種語言消息的基本工具。查看文檔中關於ResourceBundle 類以及你的JDK版本的文檔包中關於國際化的更多內容。 
PropertyResourceBundle - 一個 ResourceBundle 類的標準實現允許你使用與初始化properties文件同樣的“name=value”語法來定義資源。這對於使用爲用於一個web應用程序的消息準備資源包是非常方便的,因爲這些消息通常都是面向文本的。 
MessageFormat - java.text.MessageFormat 類允許你使用運行時指定的參數替換一個消息字符串中的一部分(在這種情況下,是一個從一個資源包得到的消息)。這在你創建一個句子的場合中是有用的,但是詞會以不同的語言按照不同的順序出現。消息中的佔位符字符串{0}用第一個運行時參數替換,{1}用第二個運行時參數替換,以此類推。 
MessageResources - Struts的類 org.apache.struts.util.MessageResources 使你能夠將一套資源包視做一個數據庫,並且允許你爲一個特定的Locale(通常是與當前用戶相對應)請求一個特定的消息,而不是爲服務器運行在其中的缺省的Locale請求消息。 
對於一個國際化的應用程序,遵照JDK文檔包中國際化文檔所描述的步驟來創建一個包含每種語言的消息的屬性文件。下面舉一個例子說明。 
假設你的源代碼建立在包 com.mycompany.mypackage 中,因此它保存於一個叫做(相對於你的源目錄)com/mycompany/mypackage 的目錄中。爲創建一個叫做 com.mycompany.mypackage.MyResources 的資源包,你應該在目錄 com/mycompany/mypackage 中創建下列文件: 
MyResources.properties - 包含你的服務器的缺省語言的消息。如果你的缺省語言是英語,你可能有一個這樣的條目: 
prompt.hello=Hello
MyResources_xx.properties - 包含ISO語言編程爲“xx”(查看ResourceBundle的Java文檔頁面得到一個當前列表的聯接)的同樣的消息。對於上面的消息的法語版,你可以有這個條目: 
prompt.hello=Bonjour
你可以有你需要的任意多的語言的資源包文件。
當你在web應用程序展開描述符中配置controller servlet時,你需要在一個初始化參數中定義的一件事是應用程序的資源包的基礎名。在上述的情況中,這應該是 com.mycompany.mypackage.MyResources 。 

3.3 表單和FormBean的交互

大部分web開發者曾經使用HTML的標準性能來建立表單,例如使用 <input> 標記。用戶希望交互程序具有一定的行爲,這些期待中的一個與錯誤處理有關 -- 如果用戶出現一個錯誤,應用程序應該允許他們僅僅修改需要修改的部分 -- 而不需要重新敲入當前頁面或表單中的任何其它信息。 
使用標準的HTML和JSP編程來完全實現這個期望是單調而繁重的。舉例來說,一個用戶名字段的輸入元素看起來可以象是這樣(在JSP中) 
<input type="text" name="username"
value="<%= loginBean.getUsername() %>">
這很難敲對,會把沒有編程概念的HTML開發者搞糊塗,並且會在HTML編輯器中造成問題。取而代之的是,Struts提供了一種全面的基於JSP 1.1的定製標記庫功能的機制來建立表單。上面的情況使用Struts處理後將象是這樣: 
<struts:text name="username"/>
沒有必要再顯式地涉及到從中獲得初始值的JavaBean。這將由框架自動處理。 

3.3.1 使用Struts建立表單

一個完整的註冊表單將演示Struts相對於直接使用HTML和標準的JSP功能怎樣極大地減輕了處理表單的痛苦。考慮以下稱爲logon.jsp的頁面(來自Struts的例子程序): 
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
<html>
<head>
<title><struts:message key="logon.title"/></title>
<body bgcolor="white">

<struts:errors/>

<struts:form action="logon.do" name="logonForm"
type="org.apache.struts.example.LogonForm"/>
<table border="0" width="100%">
<tr>
<th align="right">
<struts:message key="prompt.username"/>
</th>
<td align="left">
<struts:text name="username" size="16"/>
</td>
</tr>
<tr>
<th align="right">
<struts:message key="prompt.password"/>
</th>
<td align="left">
<struts:password name="password" size="16"/>
</td>
</tr>
<tr>
<td align="right">
<struts:submit>
<struts:message key="button.submit"/>
</struts:submit>
</td>
<td align="right">
<struts:reset>
<struts:message key="button.reset"/>
</struts:reset>
</td>
</tr>
</table>
</struts:form>

</body>
</html>
下面的條目基於這個例子演示在Struts中處理表單的關鍵的特性: 
taglib指令告訴JSP頁面編譯器從哪裏找到Struts標記庫的 標記庫描述符 。在這種情況下,我們使用struts作爲前綴來標識來自這個庫中的標記,但是可以使用任何你想用的前綴。 
這個頁面使用了幾個 message 標記來從一個包含有這個應用程序所有資源的 MessageResources 對象中查找國際化的消息字符串。爲了讓這個頁面能夠工作,以下的消息關鍵字必須在這些資源中被定義: 
logon.title - 註冊頁面的標題 
prompt.username - 一個 “Username:” 提示字符串 
prompt.password - 一個 “Password:” 提示字符串 
button.submit - “Submit”按鈕的標籤 
button.reset - “Reset”按鈕的標籤 
當用戶註冊時,應用程序可以在用戶的session中保存一個 Locale 對象。這個 Locale 將用來選擇適當語言的消息。這使得給用戶一個切換語言的可選項實現起來變的容易了 -- 僅僅改變保存的 Locale 對象,所有的消息就會自動切換。 
errors 標記顯示由一個商業邏輯組件保存的任何出錯消息,或者如果沒有出錯消息保存就什麼都沒有。這個標記將在下面做深入的描述。 
form 標記基於指定的屬性對一個HTML <form> 元素進行處理。它也將所有在這個表單中的字段與一個保存在關鍵字 logonForm 下的session範圍的FormBean相關聯。這個bean用來爲所有的具有與bean中的屬性名匹配的名字的輸入字段提供初始值。如果適當的bean沒有找到,一個新的bean將會被自動建立,使用指定的Java類名。 
text 標記對一個類型爲“text”的HTML <input> 元素進行處理。在這種情況下,佔據瀏覽器屏幕的字符位置的數字也被指定。當頁面被執行時,是相對應的bean的 username 屬性的當前值(也就是 getUsername() 的返回值)。 
password 標記使用方法類似。不同之處是當用戶敲入他們的口令時瀏覽器將回應星號字符,而不是輸入值。 
submit 和 reset 標記在表單低部生成相應的按鈕。每個按鈕的文本標籤使用 message 標記建立,同時帶有提示,這樣這些值就是國際化的。 

3.3.2 輸入字段類型支持

Struts爲所有以下類型的輸入字段定義了標記,帶有與其相應的參考信息的超聯接。 
checkboxes 
hidden 字段 
password 輸入字段 
radio 按鈕 
reset 按鈕 
select 列表和嵌入的 
options 
submit 按鈕 
text 輸入字段 
textareas 
在所有情況下,一個字段標記都必須嵌套在一個 form 標記中,這樣字段才知道使用哪個bean來初始化顯示的值。 

3.3.3 其它有用的表示標記

在Struts的標記庫中有幾個其它的標記對於建立用戶界面是有幫助的: 
enumerate 爲一個指定集合的每個元素重複一次標記體(可以是一個Enumeration,一個Hashtable,一個Vector或一個對象數組)。 
getProperty 從指定的bean中得到指定的屬性,並且在本頁面的其餘部分作爲一個page範圍的bean存在。這是訪問一個被 enumerate 使用的集合的方便的方法。 
ifAttributeExists 只有在一個指定的屬性存在於一個指定的範圍中時纔對標記體求值。 
ifAttributeMissing 只有在一個指定的屬性不存在於一個指定的範圍中時纔對標記體求值。 
ifParameterEquals 只有在一個指定的請求參數具有一個指定的值時纔對標記體求值。 
ifParameterNotEquals 只有在一個指定的請求參數不具有一個指定的值或者不存在時纔對標記體求值。 
ifParameterNotNull 只有在一個指定的請求參數包含在這個請求中並且長度大於0時纔對標記體求值。 
ifParameterNull 只有在一個指定的請求參數不包含在這個請求中或者長度等於0時纔對標記體求值。 
iterate 爲一個指定集合中的每個元素重複一次標記體(可以是一個Collection,一個Iterator,一個Map,或者一個對象數組)。這個標記在Java2環境中代替了 enumerate 標記。 
link 生成一個超聯接,當沒有cookie支持時自動應用URL編程來維護session狀態。 
parameter 處理指定請求參數的值,適當地過濾HTML中有特殊含義的字符。 
property 顯示一個表單中命名的bean屬性 -- 在屬性應該是隻讀時使用這個標記而不是 text 標記。 

3.3.4 自動錶單驗證

除了上面描述的表單和bean的交互外,如果你的bean知道怎樣驗證它接收的輸入字段,Struts還提供一種附加的機制。爲了利用這個特性,使你的bean類實現 ValidatingActionForm 接口,而不是 ActionForm 接口。一個 ValidatingActionForm 增加了一個附加的方法簽名: 
public String[] validate()
對於一個被controller servlet在bean屬性已經組裝但是在相應的行爲類的 perform() 方法被調用之前調用的方法,validate() 方法有以下可選項: 
執行適當的驗證發現沒有錯誤 -- 返回 null 或者一個非0長度字符串數組,並且controller servlet將繼續調用適當的 Action 類的 perform() 方法。 
執行適當的驗證發現有錯誤 -- 返回一個內容爲應該被顯示的出錯消息關鍵字(進入應用程序的MessageResources 包)的字符串數組。controller servlet將作爲適合於 <struts:errors> 標記使用的請求屬性保存這個數組,並且將控制重定向回輸入表單(由這個 ActionMapping 的 inputForm 屬性標識)。 
正如以前提到的,這個特性完全是可選的。如果你的form bean 僅僅實現了 ActionForm 接口,controller servlet將假設任何請求的驗證由action類完成。 

3.4 其它的表示技術

儘管你的應用程序的外表和感覺可以完全基於標準的JSP能力和Struts的定製標記庫構建,你也應該考慮展開其它改進組件重用、減少管理負擔或者減少出錯的技術。在下面的部分討論幾個可選的技術。 

3.4.1 特定於應用程序的定製標記

在使用Struts庫提供的定製標記之外,很容易建立特定於你創建的應用程序的標記來幫助建立用戶界面。Struts包括的例子程序用建立以下僅用於實現這個應用程序的標記演示了這個原則: 
checkLogon - 檢查一個特殊的會話對象的存在,如果不存在將控制重定向到註冊頁面。這是用來捕捉這樣的情況,用戶在你的應用程序執行的中間把一個頁面做成書籤並且試圖跳過註冊,或者用戶的會話超時。 
linkSubscription - 爲一個詳細的定單頁面生成一個超聯接,它將需要的主關鍵字值作爲一個請求屬性傳遞。這在列出與一個用戶相關的定單並提供編輯或刪除定單的聯接時使用。 
linkUser - 爲一個用戶的一個具體的頁面生成一個超聯接,它將它將需要的主關鍵字值作爲一個請求屬性傳遞。 
這些標記的源代碼在 src/example 目錄中,在包 org.apache.struts.example 裏,還帶有一些其它的用在這個應用程序中的Java類。 

3.4.2 有包含文件的頁面組件

在一個JSP文件(包含定製標記和beans用來訪問請求的動態數據)中創建完整的表示是一種非常普通的設計方法,在Struts包括的例子程序中被採用。然而很多應用程序要求在單獨一個頁面中顯示你的應用程序的多個邏輯上獨立的部分。 
舉例來說,一個入口應用程序可以在入口的主頁面上有一些或者全部以下的功能: 
訪問這個入口的一個搜索引擎。 
一個或更多的“提供新聞”的顯示,含有按照用戶的註冊信息定製的感興趣的標題。 
訪問與這個入口相關的討論的主題。 
如果你的入口提供免費郵件帳號,還要有一個“郵件等待”的提示。 
如果你能夠將工作劃分開,分配不同的開發者去做不同的片段,那麼這個站點不同片段的開發就會更加簡單。然後,你可以使用JSP技術的 include 能力來將這些片段組合進一個單獨的頁面。有兩種 include 可用,依賴於你希望輸出的組合發生在什麼時間: 
include 指令 (<%@ include file="xxxxx" %>)在JSP頁面被編譯時處理。它用於包括不需要在請求時改變的HTML代碼。它把包括進來的文本當作靜態文本,很象C或C++中的 #include 指令。 
include 行爲 (<jsp:include page="xxxxx" flush="true" />)在請求時處理,並且是由服務器透明處理。這意味着你可以通過把它嵌套在一個類似ifParameterEquals的標記中有條件地執行include 。 

3.4.3 圖片處理組件

一些應用程序要求動態生成圖片,就象一個股市報告站點的價格圖一樣。通常使用兩種不同的方法來實現這個需求: 
處理一個執行一個servlet請求的URL的超聯接。這個servlet將使用一個圖象庫來生成圖片,設置適當的content類型(例如 image/gif),並且將圖片的字節流發送回瀏覽器。瀏覽器就會象從一個靜態文件中接收到的一樣顯示圖片。 
處理HTML代碼需要下載一個創建請求的圖象的Java applet。你可以通過爲在處理的代碼中的這個applet設置適當的初始化參數配置這個圖象,或者你可以讓這個applet與服務器建立自己聯接來接收這些參數。 

4. 創建Controller組件

4.1 概述

現在我們理解了怎樣構造你的應用程序的Model和View組件,現在是集中到 Controller 組件的時候了。Struts包括一個實現映射一個請求URI到一個行爲類的主要功能的servlet。因此你的與Controller有關的主要責任是: 
爲每一個可能接收的邏輯請求寫一個 Action 類(也就是,一個 Action 接口的實現) 
寫一個定義類名和與每個可能的映射相關的其它信息的 ActionMapping 類(也就是,一個 ActionMapping 接口的實現) 
寫行爲映射配置文件(用XML)用來配置controller servlet。 
爲你的應用程序更新web應用程序展開描述符文件(用XML)用來包括必需的Struts組件。 
給你的應用程序添加適當的Struts組件。 

4.2 Action類

Action 接口定義一個單一的必須由一個 Action 類實現的方法,就象下面這樣: 
public ActionForward perform(ActionServlet servlet,
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
一個 Action 類的目標是處理這個請求,然後返回一個標識JSP頁面的 ActionForward 對象,控制應該重定向這個JSP頁面以生成相應的響應。在 Model 2 設計模式中,一個典型的 Action 類將在它的 perform() 方法中實現下面的邏輯: 
驗證用戶session的當前狀態(例如,檢查用戶已經成功地註冊)。如果 Action 類發現沒有註冊存在,請求應該重定向到顯示用戶名和口令用於註冊的JSP頁面。應該這樣做是因爲用戶可能試圖從“中間”(也就是,從一個書籤)進入你的應用程序,或者因爲session已經超時並且servlet容器創建了一個新的session。 
如果驗證還沒有發生(由於使用一個實現 ValidatingActionForm 接口的form bean),驗證這個 form bean 的屬性是必須的。如果發現一個問題,當作一個請求屬性保存合適的出錯信息關鍵字,然後將控制重定向回輸入表單這樣錯誤可以被糾正。 
執行要求的處理來處理這個請求(例如在數據庫裏保存一行)。這可以用嵌入 Action 類本身的代碼來完成,但是通常應該調用一個商業邏輯bean的一個合適的方法來執行。 
更新將用來創建下一個用戶界面頁面的服務器端對象(典型情況下是request範圍或session範圍beans,定義你需要在多長時間內保持這些項目可獲得)。 
返回一個標識生成響應的JSP頁面的適當的 ActionForward 對象,基於新近更新的beans。典型情況下,你將通過在你接收到的 ActionMapping 對象(如果你使用一個局部於與這個映射上的邏輯名)或者在controller servlet 本身(如果你使用一個全局於應用程序的邏輯名)上調用 findForward() 得到一個對這樣一個對象的引用。 
當爲 Action 類編程時要記住的設計要點包括以下這些: 
controller servlet僅僅創建一個你的 Action 類的實例,用於所有的請求。這樣你需要編寫你的 Action 類使其能夠在一個多線程環境中正確運行,就象你必須安全地編寫一個servlet的 service() 方法一樣。 
幫助線程安全編程的最重要的原則就是在你的 Action 類中僅僅使用局部變量而不是實例變量。局部變量創建於一個分配給(由你的JVM)每個請求線程的棧中,所以沒有必要擔心會共享它們。 
儘管不應該,代表你的系統中Model部分的的beans仍有可能拋出違例。你應該在你的 perform() 方法的邏輯中捕捉所有這樣的違例,並且通過執行以下語句將它們記錄在應用程序的日誌文件中(包括相應的棧跟蹤信息): 
servlet.log("Error message text", exception);
作爲一個通用的規則,分配很少的資源並在來自同一個用戶(在用戶的session中)的請求間保持它們會導致可伸縮性的問題。你應該在將控制重定向到適當的View組件前努力釋放這樣的資源(例如數據庫聯接) -- 甚至在你調用的一個bean拋出了一個違例時。 
另外,你將會想要防止出現非常大的 Action 類。最簡單的實現途徑是將你的功能邏輯嵌入到 Action 類本身,而不是將其寫在獨立的商業邏輯beans中。除了使 Action 類難於理解和維護外,這種方法也使得難於重用這些商業邏輯代碼,因爲代碼被嵌入到一個組件(Action 類)中並被捆綁運行於web應用程序環境中。 
包括在Struts中的例子程序某種程度上延伸了這個設計原則,因爲商業邏輯本身是嵌入到 Action 類中的。這應該被看作是在這個樣本應用程序設計中的一個bug,而不是一個Struts體系結構中的固有特性,或者是一個值得仿效的方法。 

4.3 ActionMapping實現

爲了成功地運行,Struts的controller servlet需要知道關於每個URI該怎樣映射到一個適當的 Action 類的幾件事。需要了解的知識封裝在一個叫做 ActionMapping 的Java接口中,它有以下屬性: 
actionClass - 用於這個映射的 Action 類完整的Java類名。第一次一個特定的映射被使用,一個這個類的實例將被創建併爲以後重用而保存。 
formAttribute - session範圍的bean的名字,當前的這個映射的 ActionForm 被保存在這個bean之下。如果這個屬性沒有被定義,沒有 ActionForm 被使用。 
formClass - 用於這個映射的 ActionForm 類完整的Java類名。如果你在使用對form beans的支持,這個類的一個實例將被創建並保存(在當前的用戶會話中) 
path - 匹配選擇這個映射的請求的URI路徑。看下面如何匹配的例子。 
Struts在一個叫做 ActionMappingBase 的類中包括了一個 ActionMapping 接口的方便的實現。如果你不需要爲你自己的映射定義任何附加的屬性,儘管把這個類作爲你的 ActionMapping 類好了,就向下面部分描述的那樣配置。然而,定義一個 ActionMapping 實現(多半是擴展 ActionMappingBase 類)來包含附加的屬性也是可能的。controller servlet知道怎樣自動配置這些定製屬性,因爲它使用Struts的Digester模塊來讀配置文件。 
包括在Struts的例子程序中,這個特性用來定義兩個附加的屬性: 
failure - 如果Action類檢測到它接收的輸入字段的一些問題,控制應該被重定向到的上下文相關的URI。典型情況下是請求發向的JSP頁面名,它將引起表單被重新顯示(包含Action類設置的出錯消息和大部分最近的來自ActionForm bean的輸入值)。 
success - 如果Action類成功執行請求的功能,控制應該被重定向到的上下文相關的URI。典型情況下是準備這個應用程序的會話流的下一個頁面的JSP頁面名。 
使用這兩個額外的屬性,例子程序中的 Action 類幾乎完全獨立於頁面設計者使用的實際的JSP頁面名。這個頁面可以在重新設計時被重命名,然而幾乎不會影響到 Action 類本身。如果“下一個”JSP頁面的名字被硬編碼到 Action 類中,所有的這些類也需要被修改。 

4.4 Action映射配置文件

controller servlet怎樣知道你想要得到的映射?寫一個簡單地初始化新的 ActionMapping 實例並且調用所有適當的set方法的小的Java類是可能的(但是很麻煩)。爲了使這個處理簡單些,Struts包括一個Digester模塊能夠處理一個想得到的映射的基於XML的描述,同時創建適當的對象。看 API 文檔 以獲得關於Digester更多的信息。 
開發者的責任是創建一個叫做 action.xml 的XML文件,並且把它放在你的應用程序的WEB-INF目錄中。(注意這個文件並不需要 DTD,因爲實際使用的屬性對於不同的用戶可以是不同的)最外面的XML元素必須是<action-mappings>,在這個元素之中是嵌入的0個或更多的 <action> 元素 -- 每一個對應於你希望定義的一個映射。 

來自例子程序的 action.xml 文件包括“註冊”功能的以下映射條目,我們用來說明這個需求: 

<action-mappings>

<forward name="logon" path="/logon.jsp"/>

<action path="/logon"
actionClass="org.apache.struts.example.LogonAction"
formAttribute="logonForm"
formClass="org.apache.struts.example.LogonForm"
inputForm="/logon.jsp">
<forward name="success" path="/mainMenu.jsp"/>
</action>

</action-mappings>
就象你所看到的,這個映射匹配路徑 /logon (實際上,因爲例子程序使用擴展匹配,你在一個JSP頁面指定的請求的URI結束於/logon.do)。當接收到一個匹配這個路徑的請求時,一個 LogonAction 類的實例將被創建(僅僅在第一次)並被使用。controller servlet將在關鍵字 logonForm 下查找一個session範圍的bean,如果需要就爲指定的類創建並保存一個bean。 
這個 action 元素也定義了一個邏輯名“success”,它在 LogonAction 類中被用來標識當一個用戶成功註冊時使用的頁面。象這樣使用一個邏輯名允許將 action 類隔離於任何由於重新設計位置而可能發生的頁面名改變。 

這是第二個在任何 action 之外宣告的 forward 元素,這樣它就可以被所有的action全局地獲得。在這個情況下,它爲註冊頁面定義了一個邏輯名。當你調用 mapping.findForward() 時在你的 action 代碼中,Struts首先查找這個action本地定義的邏輯名。如果沒有找到,Struts會自動爲你查找全局定義的邏輯名。 

4.5 Web應用程序展開描述符

設置應用程序最後的步驟是配置應用程序展開描述符(保存在文件WEB-INF/web.xml中)以包括所有必需的Struts組件。作爲一個指南使用例子程序的展開描述符,我們看到下面的條目需要被創建或修改。 

4.5.1 配置Action Servlet實例

添加一個條目定義action servlet本身,同時包括適當的初始化參數。這樣一個條目看起來象是這樣: 
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-p
aram>
<param-name>application</param-name>
<param-value>org.apache.struts.example.ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/action.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>mapping</param-name>
<param-value>org.apache.struts.example.ApplicationMapping</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
controller servlet支持的初始化參數在下面描述,拷貝自 ActionServlet 類的 Javadocs 。方括號描述如果你沒有爲那個初始化參數提供一個值時假設的缺省值。 
application - 應用程序資源包基類的Java類名。[NONE]. 
config - 包含配置信息的XML資源的上下文相關的路徑。[/WEB-INF/action.xml] 
debug - 這個servlet的調試級別,它控制記錄多少信息到日誌中。[0] 
digester - 我們在 initMapping() 中利用的Digester的調試級別,它記錄到System.out而不是servlet的日誌中。[0] 
forward - 使用的ActionForward實現的Java類名。[org.apache.struts.action.ActionForward] 
mapping - 使用的ActionMapping實現的Java類名。[org.apache.struts.action.ActionMappingBase] 
nocache - 如果設置爲 true,增加HTTP頭信息到所有響應中使瀏覽器對於生成或重定向到的任何響應不做緩衝。[false] 
null - 如果設置爲 true,設置應用程序資源使得如果未知的消息關鍵字被使用則返回 null。否則,一個包括不歡迎的消息關鍵字的出錯消息將被返回。[true] 

4.5.2 配置Action Servlet映射

有兩種通常的方法來定義將被controller servlet處理的URL -- 前綴匹配和擴展匹配。每種方法的一個適當的映射條目將在下面被描述。 
前綴匹配意思是你想讓所有以一個特殊值開頭(在上下文路徑部分之後)的URL傳遞給這個servlet。這樣一個條目看起來可以象是這樣: 

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
它意味着一個匹配前面描述的 /logon 路徑的請求的URL看起來象是這樣: 
http://www.mycompany.com/myapplication/execute/logon
這裏 /myapplicationis 是你的應用程序展開所在的上下文路徑。 
另一方面,擴展映射基於URL以一個跟着定義的一組字符的句點結束的事實而將URL匹配到action servlet 。例如,JSP處理servlet映射到 *.jsp 模式這樣它在每個JSP頁面請求時被調用。爲了使用 *.do 擴展(它意味着“做某件事”)映射條目看起來應該象是這樣: 

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
並且一個匹配以前描述的 /logon 路徑的請求的URI可以看起來象是這樣: 
http://www.mycompany.com/myapplication/logon.do

4.5.3 配置Struts標記庫

下一步,你必須添加一個定義Struts標記庫的條目。這個條目看起來應該象是這樣: 
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>
它告訴JSP系統到哪裏去找這個庫的標記庫描述符(在你的應用程序的WEB-INF目錄,而不是在外部互聯網上的某個地方)。 

4.5.4 添加Struts組件到你的應用程序中

爲了在你的應用程序運行時使用Struts,你必須將 struts.tld 文件拷貝到你的 WEB-INF 目錄,將 struts.jar 文件拷貝到你的 WEB-INF/lib 。 ? 



(2)
瞭解MVC

MVC是Model,View,Controller的縮寫,MVC是Application開發的設計模式,也就是大家
所知道的Model2.在MVC的設計模式中,要求在Application開發中你把商業邏輯,界面
顯示,數據分離。也就是分別在Model,View,Controller實現:數據,控制(商業邏輯)
顯示(頁面顯示).
在以前或者說傳統的Web Application開發方式當中,如Asp,Php,Jsp(Model 1)開發當中,我們在Asp(Php,Jsp)中實現一切,如:從數據庫中取到我們需要的數據,並根據數據之間的關聯和實際的需要按照某種方式把他顯示在頁面中以及從頁面提交的表單中提取數據,根據 商業邏輯從數據庫查詢相關數據,或者把數據寫入數據庫。也就是說我們在Asp(Php,Jsp) 實現一切包括:界面顯示,商業邏輯,數據存取。這樣帶來的後果就是你所寫的Asp(Php,Jsp) 沒有層次,並且Html和Script(JavaScript、JScript,Asp、Php、Jsp源代碼)相互嵌套.可 維護性差,最要命的是在Web Application通常顯示一塊是由美工完成的,很多時候也是 你先寫好Asp、Php、Jsp然後美工進行美化,很有可能你發現經過美工處理完以後你的代碼 已經面目全非了。你不得不把你的代碼重新組織。
在MVC模式中這個問題的解決辦法是:View中負責顯示,View一般從Controller得到已經處理 過的數據,然後顯示在頁面當中,應該說這樣在Html中嵌套很少的Script.基本上美工的修改 不大會廢掉你的勞動成果。
在使用Java開發Web Application有幾種符合MVC設計模式的開發方式讓你選擇。
1:Jsp+Servlet+JavaBean(EJB)
2:Jsp+JavaBean(Controller)+JavaBean(EJB)(Model)
3:TDK(Turbine,Velocity...)
4:Xsp
5:Jsp+Struts+JavaBean(EJB)
我個人認爲後面兩種比較好,其他幾種都有可取的地方特別是使用TDK因爲有一個比較好的
工具可以自動生成很多代碼,至於它的缺點在後面幾種開發方式的比較當中我會介紹。

Struts1.1的新功能
Struts1.1與1.0相比加了一些很不錯的功能。最主要是表單驗證上功能增強。在Struts1.1
數據的驗證不象以前在Action中在validator具體實現,而是在validation.xml通過配置實現
這樣做的好處就是重用性加強了很多。

Struts1.1實現的主要組成
主要包括:Action,ActionForm,ActionMapping,ActionForward,開發當中最主要寫的是Action
ActionForm根據需要可以寫或不寫。下面我就一一具體介紹。
Action
An Action is an adapter between the contents of an incoming HTTP request
and the corresponding business logic that should be executed to process this
request.
上面是Struts開發小組對Action的描述,說Action實際上是Request和Business Logic
中間的適配器.通俗的說就是從表單中取到數據並穿給商業邏輯操作進行一系列的操作
然後返回相應的操作信息。

ActionForm
An ActionForm is a JavaBean optionally associated with one or more
ActionMappings. Such a bean will have had its properties initialized from
the corresponding request parameters before the corresonding action's execute()
method is called.
ActionForm實際上就是把從Request取到的數據封裝並進行校驗,然後把合法的數據給
Action進行處理。實際上ActionForm除了進行數據校驗之外另外更重要的是在表單回寫
的時候作用很大。反而在1.1以後數據校驗的大部分工作在validation.xml去實現。

ActionMapping,ActionForward
ActionMapping主要是用與配置和描述相關屬性使用的。先看下在struts-config.xml
中的配置文件一段配置描述:
<action-mappings>
<!-- Registration Action -->
<action path="/usereg"
type="com.bingo.finance.action.UseregAction"
name="useregForm"
scope="request"
validate="true"
input="/usereg.jsp">
<forward name="success" path="/msg.jsp"/>
</action>
</action-mappings>
ActionMapping就是用來描述一個Action的URL、具體實現的文件、相對應的ActionForm
數據屬性(request or session)、是否需要進行數據校驗和回寫、以及處理完成後可能
跳轉的URL.
而ActionForward你就可以理解爲Action 操作完成後的跳轉URL,Action在處理完相關操作後
返回的是一個ActionForward也就是告訴Struts我做完這個操作下一步到哪兒去。


構建Struts1.1運行環境
我的配置是居於Tomcat4.0以上版本討論,其他的AppServer大致相同。
1:得到Struts1.1
http://jakarta.apache.org/builds/jakarta-struts/release/v1.1-b1/jakarta-struts-1.1-b1.zip
2:設置
把Struts.jar Copy到$Tomcat_home/common/lib 或你使用Struts的Appaction下的WEB-INF/lib下
在你使用Struts的Appaction下web.xml中增加下列配置

<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>

<!-- Nested Tag Library Descriptor -->
<taglib>
<taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>

<!-- Template Tag Library Descriptor -->
<taglib>
<taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-template.tld</taglib-location>
</taglib>
Struts1.1中提供了很詳細的例子,你可以仔細看看.
接下來你該根據需要配置struts-config.xml,以下是一個簡單的例子
<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

<!-- ========== Form Bean Definitions =================================== -->
<form-beans>

<!-- Registration form bean -->
<form-bean name="useregForm"
type="com.bingo.finance.action.UserForm"/>

</form-beans>

<!-- ========== Global Forward Definitions ============================== -->
<global-forwards>
<forward name="error" path="/error.jsp"/>
</global-forwards>

<!-- ========== Action Mapping Definitions ============================== -->
<action-mappings>

<!-- Registration Action -->
<action path="/usereg"
type="com.bingo.finance.action.UseregAction"
name="useregForm"
scope="request"
validate="true"
input="/usereg.jsp">
<forward name="success" path="/msg.jsp"/>
</action>
</action-mappings>

<!-- ========== Message Resources Definitions =========================== -->

<message-resources
parameter="com.bingo.finance.common.DisplayMsg"/>

<!-- ========== Plug Ins Configuration ================================== -->

<!-- Add multiple validator resource files by setting the pathname property -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathname" value="/WEB-INF/validator-rules.xml"/>
<set-property property="pathname" value="/WEB-INF/validation.xml"/>
</plug-in>

</struts-config>
上面的英文我相信你能夠看懂。我就不做解釋了。你需要繼續配置validation.xml了,看如下
簡單的例子.
<form-validation>
<formset>
<form name="useregForm">
<field property="username"
depends="required,mask,minlength,maxlength">
<arg0 key="common_username"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^/w+$</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>5</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>20</var-value>
</var>
</field>
<field property="password"
depends="required,mask,minlength,maxlength">
<arg0 key="common_password"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^/w+$</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>5</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>20</var-value>
</var>
</field>
<field property="nickname"
depends="required,mask,minlength,maxlength">
<arg0 key="common_nickname"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^/w+$</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>5</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>20</var-value>
</var>
</field>
<field property="superpass"
depends="required,mask,minlength,maxlength">
<arg0 key="common_superpass"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^/w+$</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>10</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>20</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
上面validation.xml就是告訴Struts我的useregForm取到的數據應該做下面的驗證
username是必須不能爲空的並且最小長度爲5,最大長度是20.
................
password,nickname,superpass基本一樣我就不做更多說明.至次配置基本結束,我們要開始寫
第一個Struts 了。

開發Struts1.1

usereg.jsp
爲了考慮頁面的靈活性,在頁面中顯示的所有元素我都放在properties文件中並由
com.bingo.finance.common.HtmlMsg這個文件取到.
===================================================================
<%@ page contentType="text/html;charset=GBK" %>
<%@ page import="java.io.*"%>
<%@ page import="com.bingo.finance.common.HtmlMsg"%>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
<head>
<title><%=HtmlMsg.TITLE_REGISET%></title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>

<body bgcolor="#FFFFFF" text="#000000">
<form name="usereg" method="post" action="usereg.esp">
<!--在Struts中默認的後綴是.do你可以根據需要自己修改-->
<table width="100%" border="0" cellspacing="1" cellpadding="1">
<tr align="center">
<td colspan="2"><%=HtmlMsg.TITLE_REGISET%></td>
</tr>
<tr>
<td width="49%" align="right"><%=HtmlMsg.COMMON_USERNAME%>:</td>
<td width="51%">
<!--爲了實現回寫在Struts中建議你使用Tag使用如下寫法代替一般的寫法
<html:text property="username" size="20" maxlength="30" styleClass="css" styleId="userId"/>
上面的代碼你看不懂你需要進一步瞭解Tag.
-->
<input type="text" name="username" size="20" maxlength="20">
</td>
</tr>
<tr>
<td width="49%" align="right"><%=HtmlMsg.COMMON_PASSWORD%>:</td>
<td width="51%">
<input type="password" name="password" size="20" maxlength="20">
</td>
</tr>
<tr>
<td width="49%" align="right"><%=HtmlMsg.COMMON_NICKNAME%>:</td>
<td width="51%">
<input type="text" name="nickname" size="20" maxlength="20">
</td>
</tr>
<tr>
<td width="49%" align="right"><%=HtmlMsg.COMMON_SUPERPASS%>:</td>
<td width="51%">
<input type="password" name="superpass" size="20" maxlength="20">
</td>
</tr>
<tr>
<td width="49%" align="right">
<input type="submit" name="Submit" value="<%=HtmlMsg.ACTION_REGISET%>">
</td>
<td width="51%">
<input type="reset" name="Submit2" value="<%=HtmlMsg.COMMON_RESET%>">
</td>
</tr>
</table>
</form>
</body>
</html>


UseregActiom.java
===========================================================
package com.bingo.finance.action;

//java import
import java.io.IOException;
import java.util.Locale;

//servlet import
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//struts import
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;

//finance import
import com.bingo.finance.action.UserForm;
import com.bingo.finance.manager.UserManager;
import com.bingo.finance.entity.User;

public class UseregAction extends Action {
//在Struts1.1以前使用perform
//struts1.1使用execute替代perform
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
try{
UserForm userForm=(UserForm)form;
UserManager userManager=new UserManager();
User user=userManager.formToEntity(userForm);
userManager.add(user);
//Insert into DataBase a Userinfo
}catch(Exception ex){
return mapping.findForward("error");
}
return mapping.findForward("success");
//ForWard is "/msg.jsp"
}
}

UserForm.java
=========================================================================
package com.bingo.finance.action;
import java.util.*;
import java.io.Serializable;
//servlet import
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//struts import
import org.apache.struts.action.ActionMapping;
import org.apache.struts.validator.ValidatorForm;
public class UserForm extends ValidatorForm implements Serializable{
//在Struts1.1以前通常Form繼承ActionForm
//實際上ValidatorForm是繼承ActionForm
//爲什麼要加一層ValidatorForm是爲了校驗而做的
private String id;
private String username;
private String password;
private String nickname;
private String superpass;


public UserForm(){

}

/****/
public String getId(){
return this.id;
}

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

/****/
public String getUsername(){
return this.username;
}

/****/
public void setUsername(String _username){
this.username=_username;
}

/****/
public String getPassword(){
return this.password;
}

/****/
public void setPassword(String _password){
this.password=_password;
}

/****/
public String getNickname(){
return this.nickname;
}

/****/
public void setNickname(String _nickname){
this.nickname=_nickname;
}

/****/
public String getSuperpass(){
return this.superpass;
}

/****/
public void setSuperpass(String _superpass){
this.superpass=_superpass;
}

/**show this class info**/
public String toString(){
StringBuffer info=new StringBuffer();
info.append("....id is:"+id);
info.append("....username is:"+username);
info.append("....password is:"+password);
info.append("....nickname is:"+nickname);
info.append("....superpass is:"+superpass);
return info.toString();
}

public void reset(ActionMapping mapping, HttpServletRequest request) {
id=null;
username=null;
password=null;
nickname=null;
superpass=null;
}
}

UserManager.java ,User.java文件我就不提供了,這一部分在實際的開發當中根據需要自己去
處理,也就是爲了把數據插入數據庫。
現在一個簡單的註冊用戶你就開發完成了。很簡單吧。呵呵,繼續努力...但其中還有很多
細節你需要進一步瞭解,我只把你領到門了,你必須自己去研究一些東西,比如說,我希望
我的用戶多一個Email字段和一個年齡字段我應該如何做,而且我希望validation.xml的驗證
有Email合法驗證,年齡必須大於0而且一定是整數。那我應該如何做?
我只能告訴你加兩個字段你需要修改Form 增加字段,同時相應文件也要修改。在struts中
Email的驗證非常簡單。好好看例子吧,好好研究validator-rules.xml,這個我認爲struts1.1
最好的功能增加(Struts1.0沒有這個文件)。Struts中提供了一個非常靈活而且重用極高的驗證機制。

Struts和其他開發方式的比較
使用Struts開發至少帶來如下好處:
1:層次結構非常清晰,也使得分工很明確。
2:重用度很高,連數據驗證都可以重用,還有一個更好的重用就是Form,Action是可以很好的重用的。
3:可維護性好。這是居於第一點的。
.................
下面我說一個Struts的不足之處,如果你使用或者瞭解TDK你就會覺得,TDK中可以自動生成很多
java源代碼的確可以節省不少工作量,Struts中也可以做的很好。我自己開發了一個工具,功能
還不夠強大。在我的計劃當中,我希望Form,JavaBean(封裝數據相當於EJB中的Entity Bean),
甚至包括操作數據庫的Method都自動生成,還有validation.xml也可以動態的生成。然而因爲
下一個原因:Struts到目前爲止還不是一個比較成熟的項目。他的改動實在是太大了。1.0和0.5
就有很大的改動。所以我的計劃有變。我希望在1.1完全推出來再做開發(1.1現在是Beta版)。
同時我也給Craig R. McClanahan(Struts開發的Team leader)寫過信聽取他的建議.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章