明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

Struts1.1 說(shuō)明

[摘要]前一段時(shí)間好象有人問(wèn)關(guān)于Struts的事情,本人留意了一下.請(qǐng)有興趣的同志不防研究研究.(1)1. 介紹 1.1 Model-View-Controller (MVC) 設(shè)計(jì)模式 FIXME - 需要一個(gè)對(duì)該模式一般性的介紹。(譯注:可以參考機(jī)械工業(yè)出版社的《設(shè)計(jì)模式》。)  ...
前一段時(shí)間好象有人問(wèn)關(guān)于Struts的事情,本人留意了一下.請(qǐng)有興趣的同志不防研究研究.
(1)
1. 介紹

1.1 Model-View-Controller (MVC) 設(shè)計(jì)模式

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

Struts 的體系結(jié)構(gòu)實(shí)現(xiàn)了Model-View-Controller設(shè)計(jì)模式的概念,它將這些概念映射到web應(yīng)用程序的組件和概念中!
這一體系結(jié)構(gòu)中每個(gè)主要的組件都將在下面做詳細(xì)的討論!

1.3 Model: 系統(tǒng)狀態(tài)和商業(yè)邏輯JavaBeans

基于MVC的系統(tǒng)中的 Model 部分可以細(xì)分為兩個(gè)概念 -- 系統(tǒng)的內(nèi)部狀態(tài), 能夠改變狀態(tài)的行為。用語(yǔ)法術(shù)語(yǔ)來(lái)說(shuō),我們可以把狀態(tài)信息當(dāng)作名詞(事物),把行為當(dāng)作動(dòng)詞(事物狀態(tài)的改變)。 
通常說(shuō)來(lái),你的應(yīng)用程序?qū)⑾到y(tǒng)內(nèi)部的狀態(tài)表示為一組一個(gè)或多個(gè)的JavaBeans,使用屬性(properties)來(lái)表示狀態(tài)的細(xì)節(jié)。依賴(lài)于你的應(yīng)用程序的復(fù)雜度,這些beans可以是自包含的(以某種方式知道怎樣永久地保存它們的狀態(tài)信息),或者可以是正面的(facades),知道當(dāng)被請(qǐng)求時(shí)怎樣從外部數(shù)據(jù)源(例如數(shù)據(jù)庫(kù))中取得信息。Entity EJBs通常也用來(lái)表示內(nèi)部狀態(tài)。
大型應(yīng)用程序經(jīng)常將系統(tǒng)可能的商業(yè)邏輯行為表示為可以被維護(hù)狀態(tài)信息的beans調(diào)用的方法。舉個(gè)例子,你有一個(gè)為每個(gè)當(dāng)前用戶(hù)保存在session中的購(gòu)物車(chē)bean,里面是表示當(dāng)前用戶(hù)決定購(gòu)買(mǎi)物品的屬性。這個(gè)bean有一個(gè)checkOut()方法用來(lái)驗(yàn)證用戶(hù)的信用卡,將定單發(fā)給庫(kù)房以選擇貨品和出貨。別的系統(tǒng)分別地表示同樣的行為,或許使用Session EJBs。
在一些小型應(yīng)用程序中,同樣的行為又可能嵌入到作為Controller一部分的 Action 類(lèi)中。這在邏輯非常簡(jiǎn)單或者并不想要在其它環(huán)境中重用這些商業(yè)邏輯時(shí)是恰當(dāng)?shù)。Struts框架支持所有這些方法,但建議將商業(yè)邏輯(“做什么”)和 Action 類(lèi)(“決定做什么”)分離開(kāi)!

1.4 View: JSP頁(yè)面和表示組件

基于Struts的應(yīng)用程序中的 View 部分通常使用JSP技術(shù)來(lái)構(gòu)建。JSP頁(yè)面包含稱(chēng)為“模版文本”的靜態(tài)HTML(或XML)文本,加上插入的基于對(duì)特殊行為標(biāo)記解釋的動(dòng)態(tài)內(nèi)容。JSP環(huán)境包括了其用途由JSP規(guī)范來(lái)描述的一套標(biāo)準(zhǔn)的行為標(biāo)記,例如 <jsp:useBean> 。另外,還有一個(gè)用來(lái)定義你自己標(biāo)記的標(biāo)準(zhǔn)機(jī)制,這些自定義的標(biāo)記組織在“定制標(biāo)記庫(kù)”中。 
Struts包括了一個(gè)廣闊的便于創(chuàng)建用戶(hù)界面,并且充分國(guó)際化的定制標(biāo)記庫(kù),與作為系統(tǒng) Model 部分一部分的ActionForm beans美妙地相互配合。這些標(biāo)記的使用將在后面做詳細(xì)討論。
除了JSP頁(yè)面和其包含的行為及定制標(biāo)記,商業(yè)對(duì)象經(jīng)常需要能夠基于它們?cè)诒徽?qǐng)求時(shí)的當(dāng)前狀態(tài)將自己處理成HTML(或XML)。從這些對(duì)象處理過(guò)的輸出可以很容易地使用 <jsp:include> 標(biāo)準(zhǔn)行為標(biāo)記包括在結(jié)果的JSP頁(yè)面中!

1.5 Controller: ActionServlet和ActionMapping

應(yīng)用程序的 Controller 部分集中于從客戶(hù)端接收請(qǐng)求(典型情況下是一個(gè)運(yùn)行瀏覽器的用戶(hù)),決定執(zhí)行什么商業(yè)邏輯功能,然后將產(chǎn)生下一步用戶(hù)界面的責(zé)任委派給一個(gè)適當(dāng)?shù)腣iew組件。在Struts中,controller的基本組件是一個(gè) ActionServlet 類(lèi)的servlet。這個(gè)servlet通過(guò)定義一組映射(由Java接口 ActionMapping 描述)來(lái)配置。每個(gè)映射定義一個(gè)與所請(qǐng)求的URI相匹配的路徑和一個(gè) Action 類(lèi)(一個(gè)實(shí)現(xiàn) Action 接口的類(lèi))完整的類(lèi)名,這個(gè)類(lèi)負(fù)責(zé)執(zhí)行預(yù)期的商業(yè)邏輯,然后將控制分派給適當(dāng)?shù)腣iew組件來(lái)創(chuàng)建響應(yīng)!
Struts也支持使用包含有運(yùn)行框架所必需的標(biāo)準(zhǔn)屬性之外的附加屬性的 ActionMapping 類(lèi)的能力。這允許你保存特定于你的應(yīng)用程序的附加信息,同時(shí)仍可利用框架其余的特性。另外,Struts允許你定義控制將重定向到的邏輯名,這樣一個(gè)行為方法可以請(qǐng)求“主菜單”頁(yè)面(舉例),而不需要知道相應(yīng)的JSP頁(yè)面的實(shí)際名字是什么。這個(gè)功能極大地幫助你分離控制邏輯(下一步做什么)和顯示邏輯(相應(yīng)的頁(yè)面的名稱(chēng)是什么)!

2. 創(chuàng)建Model組件

2.1 概述

你用到的應(yīng)用程序的需求文檔很可能集中于創(chuàng)建用戶(hù)界面。然而你應(yīng)該保證每個(gè)提交的請(qǐng)求所需要的處理也要被清楚的定義。通常說(shuō)來(lái),Model 組件的開(kāi)發(fā)者集中于創(chuàng)建支持所有功能需求的JavaBeans類(lèi)。一個(gè)特殊應(yīng)用要求的beans的精確特性依賴(lài)于具體需求變化會(huì)非常的大,但是它們通?梢苑殖上旅嬗懻摰膸追N類(lèi)型。然而,首先對(duì)“范圍”概念做一個(gè)簡(jiǎn)短的回顧是有用的,因?yàn)樗cbeans有關(guān)。 

2.2 JavaBeans和范圍

在一個(gè)基于web的應(yīng)用程序中,JavaBeans可以被保存在(并從中訪問(wèn))一些不同“屬性”的集合中。每一個(gè)集合都有集合生存期和所保存的beans可見(jiàn)度的不同的規(guī)則?偟恼f(shuō)來(lái),定義生存期和可見(jiàn)度的這些規(guī)則被叫做這些beans的 范圍 。JSP規(guī)范中使用以下術(shù)語(yǔ)定義可選的范圍(在圓括號(hào)中定義servlet API中的等價(jià)物): 
page - 在一個(gè)單獨(dú)的JSP頁(yè)面中可見(jiàn)的Beans,生存期限于當(dāng)前請(qǐng)求。(service()方法中的局部變量) request - 在一個(gè)單獨(dú)的JSP頁(yè)面中可見(jiàn)的Beans,也包括所有包含于這個(gè)頁(yè)面或從這個(gè)頁(yè)面重定向到的頁(yè)面或servlet。(Request屬性) 
session - 參與一個(gè)特定的用戶(hù)session的所有的JSP和servlet都可見(jiàn)的Beans,跨越一個(gè)或多個(gè)請(qǐng)求。(Session屬性) 
application - 一個(gè)web應(yīng)用程序的所有JSP頁(yè)面和servlet都可見(jiàn)的Beans。(Servlet context屬性) 
記住同一個(gè)web應(yīng)用程序的JSP頁(yè)面和servlets共享同樣一組bean集合是很重要的。例如,一個(gè)bean作為一個(gè)request屬性保存在一個(gè)servlet中,就象這樣: 
MyCart mycart = new MyCart(...);
request.setAttribute("cart", mycart);
將立即被這個(gè)servlet重定向到的一個(gè)JSP頁(yè)面使用一個(gè)標(biāo)準(zhǔn)的行為標(biāo)記看到,就象這樣: 
<jsp:useBean id="cart" scope="request" class="com.mycompany.MyApp.MyCart"/>

2.3 ActionForm Beans

Struts框架通常假定你已經(jīng)為每一個(gè)你的應(yīng)用程序中請(qǐng)求的輸入創(chuàng)建了一個(gè) ActionForm bean(即一個(gè)實(shí)現(xiàn)了 ActionForm 接口的類(lèi))。如果你在你的 ActionMapping 配置文件中定義了這樣的beans(見(jiàn)“創(chuàng)建Controller組件”),Struts的controller servlet在調(diào)用適當(dāng)?shù)摹ction 方法前將自動(dòng)為你執(zhí)行如下的服務(wù): 
用適當(dāng)?shù)年P(guān)鍵字檢查用戶(hù)的session中是否有適當(dāng)?shù)念?lèi)的bean的一個(gè)實(shí)例!
如果沒(méi)有這樣的session范圍的bean,自動(dòng)建立一個(gè)新的bean并添加到用戶(hù)的session中!
對(duì)每個(gè)名字對(duì)應(yīng)于bean中的一個(gè)屬性的請(qǐng)求參數(shù),調(diào)用相應(yīng)的set方法。這個(gè)操作類(lèi)似于當(dāng)你以通配符“*”選擇所有屬性使用標(biāo)準(zhǔn)的JSP行為標(biāo)記 <jsp:setProperty> !
更新的ActionForm bean在被調(diào)用時(shí)將被傳遞給Acton類(lèi)的perform()方法,以使這些值能夠立即生效!
當(dāng)你在寫(xiě)你的ActionForm beans時(shí),記住以下的原則: 
ActionForm 接口本身不需要特殊的實(shí)現(xiàn)方法。它是用來(lái)標(biāo)識(shí)這些特定的beans在整個(gè)體系結(jié)構(gòu)中的作用。典型情況下,一個(gè)ActionForm bean只包括屬性的get方法和set方法,沒(méi)有商業(yè)邏輯。 
通常在一個(gè)ActionForm bean中只有很少的輸入驗(yàn)證邏輯。這樣的beans存在的主要理由是保存用戶(hù)為相關(guān)的表單所輸入的大部分近期值 -- 甚至在錯(cuò)誤被檢測(cè)到時(shí) -- 這樣同樣的頁(yè)面可以被重建,伴隨有一組出錯(cuò)信息,這樣用戶(hù)僅僅需要糾正錯(cuò)誤的字段。用戶(hù)輸入的驗(yàn)證應(yīng)該在 Action 類(lèi)中執(zhí)行(如果是很簡(jiǎn)單的話),或者在適當(dāng)?shù)纳虡I(yè)邏輯beans中執(zhí)行!
為每個(gè)表單中出現(xiàn)的字段定義一個(gè)屬性(用相關(guān)的getXxx()和setXxx()方法)。字段名和屬性名必須按照J(rèn)avaBeans的約定相匹配。例如,一個(gè)名為 username 的輸入字段將引起 setUsername() 方法被調(diào)用!
你應(yīng)該注意一個(gè)“表單”在這里討論時(shí)的意義并不必須對(duì)應(yīng)于用戶(hù)界面中的一個(gè)單獨(dú)的JSP頁(yè)面。在很多應(yīng)用程序中一個(gè)“表單”(從用戶(hù)的觀點(diǎn))延伸至多個(gè)頁(yè)面也是很平常的。想想看,例如,通常在安裝新的應(yīng)用程序時(shí)使用的導(dǎo)航安裝程序的用戶(hù)界面。Struts鼓勵(lì)你定義一個(gè)包含所有字段屬性的單獨(dú)的ActionForm bean。不管字段實(shí)際上是顯示在哪個(gè)頁(yè)面上。同樣的,同一表單的不同的頁(yè)面應(yīng)該提交到相同的Action類(lèi)。如果你遵照這個(gè)建議,在大多數(shù)情況下,頁(yè)面設(shè)計(jì)者可以重新組織不同頁(yè)面中的字段而不需要改變處理邏輯!

2.4 系統(tǒng)狀態(tài)Beans

系統(tǒng)的實(shí)際狀態(tài)通常表示為一組一個(gè)或多個(gè)的JavaBeans類(lèi),其屬性定義當(dāng)前狀態(tài)。例如,一個(gè)購(gòu)物車(chē)系統(tǒng)包括一個(gè)表示購(gòu)物車(chē)的bean,這個(gè)bean為每個(gè)單獨(dú)的購(gòu)物者維護(hù),這個(gè)bean中包括(在其它事物之中)一組購(gòu)物者當(dāng)前選擇購(gòu)買(mǎi)的項(xiàng)目。分別地,系統(tǒng)也包括保存用戶(hù)信息(包括他們的信用卡和送貨地址)、可獲得項(xiàng)目的目錄和它們當(dāng)前庫(kù)存水平的不同的beans!
對(duì)于小規(guī)模的系統(tǒng),或者對(duì)于不需要長(zhǎng)時(shí)間保存的狀態(tài)信息,一組系統(tǒng)狀態(tài)beans可以包含所有系統(tǒng)曾經(jīng)經(jīng)歷的特定細(xì)節(jié)的信息;蛘呓(jīng)常是,系統(tǒng)狀態(tài)beans表示永久保存在一些外部數(shù)據(jù)庫(kù)中的信息(例如CustomerBean對(duì)象對(duì)應(yīng)于表 CUSTOMERS 中的特定的一行),在需要時(shí)從服務(wù)器的內(nèi)存中創(chuàng)建或清除。在大規(guī)模應(yīng)用程序中,Entity EJBs也用于這種用途!

2.5 商業(yè)邏輯Beans

你應(yīng)該把你的應(yīng)用程序中的功能邏輯封裝成對(duì)為此目的設(shè)計(jì)的JavaBeans的方法調(diào)用。這些方法可以是用于系統(tǒng)狀態(tài)beans的相同的類(lèi)的一部分,或者可以是在專(zhuān)門(mén)執(zhí)行商業(yè)邏輯的獨(dú)立的類(lèi)中。在后一種情況下,你通常需要將系統(tǒng)狀態(tài)beans傳遞給這些方法作為參數(shù)處理。 
為了代碼最大的可重用性,商業(yè)邏輯beans應(yīng)該被設(shè)計(jì)和實(shí)現(xiàn)為它們不知道自己被執(zhí)行于web應(yīng)用環(huán)境中。如果你發(fā)現(xiàn)在你的bean中你必須import一個(gè) javax.servlet.* 類(lèi),你就把這個(gè)商業(yè)邏輯捆綁在了web應(yīng)用環(huán)境中?紤]重新組織事物使你的 Action 類(lèi)(Controller任務(wù)的一部分,在下面描述)翻譯所有從HTTP請(qǐng)求中請(qǐng)求被處理為對(duì)你的商業(yè)邏輯beans屬性set方法調(diào)用的信息,然后可以發(fā)出一個(gè)對(duì) execute() 的調(diào)用。這樣的一個(gè)商業(yè)邏輯類(lèi)可以被重用在除它們最初被構(gòu)造的web應(yīng)用程序以外的環(huán)境中。 
依賴(lài)于你的應(yīng)用程序的復(fù)雜度和范圍,商業(yè)邏輯beans可以是與作為參數(shù)傳遞的系統(tǒng)狀態(tài)beans交互作用的普通的JavaBeans,或者使用JDBC調(diào)用訪問(wèn)數(shù)據(jù)庫(kù)的普通的JavaBeans。而對(duì)于較大的應(yīng)用程序,這些beans經(jīng)常是有狀態(tài)或無(wú)狀態(tài)的EJBs!

2.6 題外話: 訪問(wèn)關(guān)系數(shù)據(jù)庫(kù)

很多web應(yīng)用程序利用一個(gè)關(guān)系數(shù)據(jù)庫(kù)(通過(guò)一個(gè)JDBC driver訪問(wèn))來(lái)保存應(yīng)用程序相關(guān)的永久數(shù)據(jù)。其它應(yīng)用程序則使用Entity EJBs來(lái)實(shí)現(xiàn)這個(gè)目的,他們委派EJBs自己來(lái)決定怎樣維護(hù)永久狀態(tài)。如果你是使用EJBs來(lái)實(shí)現(xiàn)這個(gè)目的,遵照EJB規(guī)范中描述的客戶(hù)端設(shè)計(jì)模式。 
對(duì)于基于直接數(shù)據(jù)庫(kù)訪問(wèn)的web應(yīng)用程序,一個(gè)普通的設(shè)計(jì)問(wèn)題是當(dāng)需要訪問(wèn)低層數(shù)據(jù)庫(kù)時(shí)怎樣產(chǎn)生一個(gè)適當(dāng)?shù)腏DBC連接對(duì)象。解決這個(gè)問(wèn)題有幾種方法 -- 以下原則描述了推薦的一種方法: 
創(chuàng)建或得到一個(gè)允許一組數(shù)據(jù)庫(kù)連接被多個(gè)用戶(hù)共享的ConnectionPool類(lèi)。Struts(當(dāng)前)沒(méi)有包括這樣的一個(gè)類(lèi),但是有很多這樣的類(lèi)可以得到!
當(dāng)應(yīng)用程序初始化時(shí),在應(yīng)用程序展開(kāi)(deployment)描述符中定義一個(gè)有一個(gè)“啟動(dòng)時(shí)加載”值的servlet。我們將把這個(gè)servlet叫做 啟動(dòng) servlet。在大多數(shù)情況下,這個(gè)servlet不需要處理任何的請(qǐng)求,所以沒(méi)有一個(gè) <servlet-mapping> 會(huì)指向它!
在啟動(dòng)servlet的 init() 方法中,配置并初始化一個(gè)ConnectionPool類(lèi)的實(shí)例,將其保存為一個(gè)servlet context屬性(從JSP的觀點(diǎn)看等同于一個(gè)application范圍的bean)。通;趥鬟f給啟動(dòng)servlet初始化參數(shù)來(lái)配置聯(lián)接緩沖池是很方便的。 
在啟動(dòng)servlet的 destroy() 方法中,包含了釋放聯(lián)接緩沖池所打開(kāi)的聯(lián)接的邏輯。這個(gè)方法將在servlet容器結(jié)束這個(gè)應(yīng)用程序的時(shí)候被調(diào)用!
當(dāng) Action 類(lèi)需要調(diào)用一個(gè)需要數(shù)據(jù)庫(kù)聯(lián)接的商業(yè)邏輯bean中的方法(例如“insert a new customer”)時(shí),將執(zhí)行下面的步驟: 
為這個(gè)web應(yīng)用程序從servelt context屬性中得到一個(gè)聯(lián)接緩沖池對(duì)象!
調(diào)用聯(lián)接緩沖池對(duì)象的 open() 方法來(lái)得到一個(gè)在 Action 類(lèi)調(diào)用中使用的聯(lián)接!
調(diào)用商業(yè)邏輯bean中合適的方法,將數(shù)據(jù)庫(kù)聯(lián)接對(duì)象作為一個(gè)參數(shù)傳遞給它。 
調(diào)用分配的聯(lián)接中的 close() 方法,這將引起這個(gè)聯(lián)接為了以后其它請(qǐng)求的重用被返回到緩沖池中!
一個(gè)通常的編程錯(cuò)誤是忘記了把數(shù)據(jù)庫(kù)聯(lián)接返回給緩沖池,這將最終導(dǎo)致用完所有的聯(lián)接。一定要確信 Action 類(lèi)的邏輯總是返回聯(lián)接,甚至在商業(yè)邏輯bean拋出一個(gè)違例時(shí)!
遵照上面推薦的設(shè)計(jì)模式意味著你能夠編寫(xiě)你的商業(yè)邏輯類(lèi)而不需要擔(dān)心它們?cè)鯓拥玫揭粋(gè)JDBC聯(lián)接來(lái)使用-- 簡(jiǎn)單地在任何需要訪問(wèn)數(shù)據(jù)庫(kù)的方法中包含一個(gè)Connection參數(shù)。當(dāng)你的商業(yè)邏輯類(lèi)在一個(gè)web應(yīng)用程序中被利用時(shí),分配和釋放適當(dāng)?shù)穆?lián)接是 Action 類(lèi)的責(zé)任。當(dāng)你使用相同的商業(yè)邏輯類(lèi)時(shí),例如,在一個(gè)批處理工作中,提供一個(gè)適當(dāng)?shù)穆?lián)接是那個(gè)應(yīng)用程序的責(zé)任(這不需要從緩沖池中獲得,因?yàn)榇蠖鄶?shù)批處理工作運(yùn)行于一個(gè)單線程環(huán)境中)!

3. 創(chuàng)建View組件

3.1 概述

這一章集中于創(chuàng)建應(yīng)用程序中的 View 組件的任務(wù),主要使用JSP技術(shù)建立。特別的,Struts除了提供了與輸入表單的交互外還提供了建立國(guó)際化應(yīng)用程序的支持。幾個(gè)其它的與View相關(guān)的主題也被簡(jiǎn)單地討論!

3.2 國(guó)際化消息

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

3.3 表單和FormBean的交互

大部分web開(kāi)發(fā)者曾經(jīng)使用HTML的標(biāo)準(zhǔn)性能來(lái)建立表單,例如使用 <input> 標(biāo)記。用戶(hù)希望交互程序具有一定的行為,這些期待中的一個(gè)與錯(cuò)誤處理有關(guān) -- 如果用戶(hù)出現(xiàn)一個(gè)錯(cuò)誤,應(yīng)用程序應(yīng)該允許他們僅僅修改需要修改的部分 -- 而不需要重新敲入當(dāng)前頁(yè)面或表單中的任何其它信息。 
使用標(biāo)準(zhǔn)的HTML和JSP編程來(lái)完全實(shí)現(xiàn)這個(gè)期望是單調(diào)而繁重的。舉例來(lái)說(shuō),一個(gè)用戶(hù)名字段的輸入元素看起來(lái)可以象是這樣(在JSP中) 
<input type="text" name="username"
value="<%= loginBean.getUsername() %>">
這很難敲對(duì),會(huì)把沒(méi)有編程概念的HTML開(kāi)發(fā)者搞糊涂,并且會(huì)在HTML編輯器中造成問(wèn)題。取而代之的是,Struts提供了一種全面的基于JSP 1.1的定制標(biāo)記庫(kù)功能的機(jī)制來(lái)建立表單。上面的情況使用Struts處理后將象是這樣: 
<struts:text name="username"/>
沒(méi)有必要再顯式地涉及到從中獲得初始值的JavaBean。這將由框架自動(dòng)處理!

3.3.1 使用Struts建立表單

一個(gè)完整的注冊(cè)表單將演示Struts相對(duì)于直接使用HTML和標(biāo)準(zhǔn)的JSP功能怎樣極大地減輕了處理表單的痛苦?紤]以下稱(chēng)為logon.jsp的頁(yè)面(來(lái)自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>

</struts:form>

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

3.3.2 輸入字段類(lèi)型支持

Struts為所有以下類(lèi)型的輸入字段定義了標(biāo)記,帶有與其相應(yīng)的參考信息的超聯(lián)接!
checkboxes 
hidden 字段 
password 輸入字段 
radio 按鈕 
reset 按鈕 
select 列表和嵌入的 
options 
submit 按鈕 
text 輸入字段 
textareas 
在所有情況下,一個(gè)字段標(biāo)記都必須嵌套在一個(gè) form 標(biāo)記中,這樣字段才知道使用哪個(gè)bean來(lái)初始化顯示的值!

3.3.3 其它有用的表示標(biāo)記

在Struts的標(biāo)記庫(kù)中有幾個(gè)其它的標(biāo)記對(duì)于建立用戶(hù)界面是有幫助的: 
enumerate 為一個(gè)指定集合的每個(gè)元素重復(fù)一次標(biāo)記體(可以是一個(gè)Enumeration,一個(gè)Hashtable,一個(gè)Vector或一個(gè)對(duì)象數(shù)組)!
getProperty 從指定的bean中得到指定的屬性,并且在本頁(yè)面的其余部分作為一個(gè)page范圍的bean存在。這是訪問(wèn)一個(gè)被 enumerate 使用的集合的方便的方法!
ifAttributeExists 只有在一個(gè)指定的屬性存在于一個(gè)指定的范圍中時(shí)才對(duì)標(biāo)記體求值!
ifAttributeMissing 只有在一個(gè)指定的屬性不存在于一個(gè)指定的范圍中時(shí)才對(duì)標(biāo)記體求值。 
ifParameterEquals 只有在一個(gè)指定的請(qǐng)求參數(shù)具有一個(gè)指定的值時(shí)才對(duì)標(biāo)記體求值!
ifParameterNotEquals 只有在一個(gè)指定的請(qǐng)求參數(shù)不具有一個(gè)指定的值或者不存在時(shí)才對(duì)標(biāo)記體求值!
ifParameterNotNull 只有在一個(gè)指定的請(qǐng)求參數(shù)包含在這個(gè)請(qǐng)求中并且長(zhǎng)度大于0時(shí)才對(duì)標(biāo)記體求值!
ifParameterNull 只有在一個(gè)指定的請(qǐng)求參數(shù)不包含在這個(gè)請(qǐng)求中或者長(zhǎng)度等于0時(shí)才對(duì)標(biāo)記體求值。 
iterate 為一個(gè)指定集合中的每個(gè)元素重復(fù)一次標(biāo)記體(可以是一個(gè)Collection,一個(gè)Iterator,一個(gè)Map,或者一個(gè)對(duì)象數(shù)組)。這個(gè)標(biāo)記在Java2環(huán)境中代替了 enumerate 標(biāo)記。 
link 生成一個(gè)超聯(lián)接,當(dāng)沒(méi)有cookie支持時(shí)自動(dòng)應(yīng)用URL編程來(lái)維護(hù)session狀態(tài)。 
parameter 處理指定請(qǐng)求參數(shù)的值,適當(dāng)?shù)剡^(guò)濾HTML中有特殊含義的字符。 
property 顯示一個(gè)表單中命名的bean屬性 -- 在屬性應(yīng)該是只讀時(shí)使用這個(gè)標(biāo)記而不是 text 標(biāo)記!

3.3.4 自動(dòng)表單驗(yàn)證

除了上面描述的表單和bean的交互外,如果你的bean知道怎樣驗(yàn)證它接收的輸入字段,Struts還提供一種附加的機(jī)制。為了利用這個(gè)特性,使你的bean類(lèi)實(shí)現(xiàn) ValidatingActionForm 接口,而不是 ActionForm 接口。一個(gè) ValidatingActionForm 增加了一個(gè)附加的方法簽名: 
public String[] validate()
對(duì)于一個(gè)被controller servlet在bean屬性已經(jīng)組裝但是在相應(yīng)的行為類(lèi)的 perform() 方法被調(diào)用之前調(diào)用的方法,validate() 方法有以下可選項(xiàng): 
執(zhí)行適當(dāng)?shù)尿?yàn)證發(fā)現(xiàn)沒(méi)有錯(cuò)誤 -- 返回 null 或者一個(gè)非0長(zhǎng)度字符串?dāng)?shù)組,并且controller servlet將繼續(xù)調(diào)用適當(dāng)?shù)摹ction 類(lèi)的 perform() 方法!
執(zhí)行適當(dāng)?shù)尿?yàn)證發(fā)現(xiàn)有錯(cuò)誤 -- 返回一個(gè)內(nèi)容為應(yīng)該被顯示的出錯(cuò)消息關(guān)鍵字(進(jìn)入應(yīng)用程序的MessageResources 包)的字符串?dāng)?shù)組。controller servlet將作為適合于 <struts:errors> 標(biāo)記使用的請(qǐng)求屬性保存這個(gè)數(shù)組,并且將控制重定向回輸入表單(由這個(gè) ActionMapping 的 inputForm 屬性標(biāo)識(shí))!
正如以前提到的,這個(gè)特性完全是可選的。如果你的form bean 僅僅實(shí)現(xiàn)了 ActionForm 接口,controller servlet將假設(shè)任何請(qǐng)求的驗(yàn)證由action類(lèi)完成!

3.4 其它的表示技術(shù)

盡管你的應(yīng)用程序的外表和感覺(jué)可以完全基于標(biāo)準(zhǔn)的JSP能力和Struts的定制標(biāo)記庫(kù)構(gòu)建,你也應(yīng)該考慮展開(kāi)其它改進(jìn)組件重用、減少管理負(fù)擔(dān)或者減少出錯(cuò)的技術(shù)。在下面的部分討論幾個(gè)可選的技術(shù)。 

3.4.1 特定于應(yīng)用程序的定制標(biāo)記

在使用Struts庫(kù)提供的定制標(biāo)記之外,很容易建立特定于你創(chuàng)建的應(yīng)用程序的標(biāo)記來(lái)幫助建立用戶(hù)界面。Struts包括的例子程序用建立以下僅用于實(shí)現(xiàn)這個(gè)應(yīng)用程序的標(biāo)記演示了這個(gè)原則: 
checkLogon - 檢查一個(gè)特殊的會(huì)話對(duì)象的存在,如果不存在將控制重定向到注冊(cè)頁(yè)面。這是用來(lái)捕捉這樣的情況,用戶(hù)在你的應(yīng)用程序執(zhí)行的中間把一個(gè)頁(yè)面做成書(shū)簽并且試圖跳過(guò)注冊(cè),或者用戶(hù)的會(huì)話超時(shí)!
linkSubscription - 為一個(gè)詳細(xì)的定單頁(yè)面生成一個(gè)超聯(lián)接,它將需要的主關(guān)鍵字值作為一個(gè)請(qǐng)求屬性傳遞。這在列出與一個(gè)用戶(hù)相關(guān)的定單并提供編輯或刪除定單的聯(lián)接時(shí)使用!
linkUser - 為一個(gè)用戶(hù)的一個(gè)具體的頁(yè)面生成一個(gè)超聯(lián)接,它將它將需要的主關(guān)鍵字值作為一個(gè)請(qǐng)求屬性傳遞。 
這些標(biāo)記的源代碼在 src/example 目錄中,在包 org.apache.struts.example 里,還帶有一些其它的用在這個(gè)應(yīng)用程序中的Java類(lèi)!

3.4.2 有包含文件的頁(yè)面組件

在一個(gè)JSP文件(包含定制標(biāo)記和beans用來(lái)訪問(wèn)請(qǐng)求的動(dòng)態(tài)數(shù)據(jù))中創(chuàng)建完整的表示是一種非常普通的設(shè)計(jì)方法,在Struts包括的例子程序中被采用。然而很多應(yīng)用程序要求在單獨(dú)一個(gè)頁(yè)面中顯示你的應(yīng)用程序的多個(gè)邏輯上獨(dú)立的部分!
舉例來(lái)說(shuō),一個(gè)入口應(yīng)用程序可以在入口的主頁(yè)面上有一些或者全部以下的功能: 
訪問(wèn)這個(gè)入口的一個(gè)搜索引擎!
一個(gè)或更多的“提供新聞”的顯示,含有按照用戶(hù)的注冊(cè)信息定制的感興趣的標(biāo)題!
訪問(wèn)與這個(gè)入口相關(guān)的討論的主題。 
如果你的入口提供免費(fèi)郵件帳號(hào),還要有一個(gè)“郵件等待”的提示!
如果你能夠?qū)⒐ぷ鲃澐珠_(kāi),分配不同的開(kāi)發(fā)者去做不同的片段,那么這個(gè)站點(diǎn)不同片段的開(kāi)發(fā)就會(huì)更加簡(jiǎn)單。然后,你可以使用JSP技術(shù)的 include 能力來(lái)將這些片段組合進(jìn)一個(gè)單獨(dú)的頁(yè)面。有兩種 include 可用,依賴(lài)于你希望輸出的組合發(fā)生在什么時(shí)間: 
include 指令。<%@ include file="xxxxx" %>)在JSP頁(yè)面被編譯時(shí)處理。它用于包括不需要在請(qǐng)求時(shí)改變的HTML代碼。它把包括進(jìn)來(lái)的文本當(dāng)作靜態(tài)文本,很象C或C++中的 #include 指令!
include 行為。<jsp:include page="xxxxx" flush="true" />)在請(qǐng)求時(shí)處理,并且是由服務(wù)器透明處理。這意味著你可以通過(guò)把它嵌套在一個(gè)類(lèi)似ifParameterEquals的標(biāo)記中有條件地執(zhí)行include !

3.4.3 圖片處理組件

一些應(yīng)用程序要求動(dòng)態(tài)生成圖片,就象一個(gè)股市報(bào)告站點(diǎn)的價(jià)格圖一樣。通常使用兩種不同的方法來(lái)實(shí)現(xiàn)這個(gè)需求: 
處理一個(gè)執(zhí)行一個(gè)servlet請(qǐng)求的URL的超聯(lián)接。這個(gè)servlet將使用一個(gè)圖象庫(kù)來(lái)生成圖片,設(shè)置適當(dāng)?shù)腸ontent類(lèi)型(例如 image/gif),并且將圖片的字節(jié)流發(fā)送回瀏覽器。瀏覽器就會(huì)象從一個(gè)靜態(tài)文件中接收到的一樣顯示圖片!
處理HTML代碼需要下載一個(gè)創(chuàng)建請(qǐng)求的圖象的Java applet。你可以通過(guò)為在處理的代碼中的這個(gè)applet設(shè)置適當(dāng)?shù)某跏蓟瘏?shù)配置這個(gè)圖象,或者你可以讓這個(gè)applet與服務(wù)器建立自己聯(lián)接來(lái)接收這些參數(shù)!

4. 創(chuàng)建Controller組件

4.1 概述

現(xiàn)在我們理解了怎樣構(gòu)造你的應(yīng)用程序的Model和View組件,現(xiàn)在是集中到 Controller 組件的時(shí)候了。Struts包括一個(gè)實(shí)現(xiàn)映射一個(gè)請(qǐng)求URI到一個(gè)行為類(lèi)的主要功能的servlet。因此你的與Controller有關(guān)的主要責(zé)任是: 
為每一個(gè)可能接收的邏輯請(qǐng)求寫(xiě)一個(gè) Action 類(lèi)(也就是,一個(gè) Action 接口的實(shí)現(xiàn)) 
寫(xiě)一個(gè)定義類(lèi)名和與每個(gè)可能的映射相關(guān)的其它信息的 ActionMapping 類(lèi)(也就是,一個(gè) ActionMapping 接口的實(shí)現(xiàn)) 
寫(xiě)行為映射配置文件(用XML)用來(lái)配置controller servlet!
為你的應(yīng)用程序更新web應(yīng)用程序展開(kāi)描述符文件(用XML)用來(lái)包括必需的Struts組件!
給你的應(yīng)用程序添加適當(dāng)?shù)腟truts組件。 

4.2 Action類(lèi)

Action 接口定義一個(gè)單一的必須由一個(gè) Action 類(lèi)實(shí)現(xiàn)的方法,就象下面這樣: 
public ActionForward perform(ActionServlet servlet,
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
一個(gè) Action 類(lèi)的目標(biāo)是處理這個(gè)請(qǐng)求,然后返回一個(gè)標(biāo)識(shí)JSP頁(yè)面的 ActionForward 對(duì)象,控制應(yīng)該重定向這個(gè)JSP頁(yè)面以生成相應(yīng)的響應(yīng)。在 Model 2 設(shè)計(jì)模式中,一個(gè)典型的 Action 類(lèi)將在它的 perform() 方法中實(shí)現(xiàn)下面的邏輯: 
驗(yàn)證用戶(hù)session的當(dāng)前狀態(tài)(例如,檢查用戶(hù)已經(jīng)成功地注冊(cè))。如果 Action 類(lèi)發(fā)現(xiàn)沒(méi)有注冊(cè)存在,請(qǐng)求應(yīng)該重定向到顯示用戶(hù)名和口令用于注冊(cè)的JSP頁(yè)面。應(yīng)該這樣做是因?yàn)橛脩?hù)可能試圖從“中間”(也就是,從一個(gè)書(shū)簽)進(jìn)入你的應(yīng)用程序,或者因?yàn)閟ession已經(jīng)超時(shí)并且servlet容器創(chuàng)建了一個(gè)新的session!
如果驗(yàn)證還沒(méi)有發(fā)生(由于使用一個(gè)實(shí)現(xiàn) ValidatingActionForm 接口的form bean),驗(yàn)證這個(gè) form bean 的屬性是必須的。如果發(fā)現(xiàn)一個(gè)問(wèn)題,當(dāng)作一個(gè)請(qǐng)求屬性保存合適的出錯(cuò)信息關(guān)鍵字,然后將控制重定向回輸入表單這樣錯(cuò)誤可以被糾正。 
執(zhí)行要求的處理來(lái)處理這個(gè)請(qǐng)求(例如在數(shù)據(jù)庫(kù)里保存一行)。這可以用嵌入 Action 類(lèi)本身的代碼來(lái)完成,但是通常應(yīng)該調(diào)用一個(gè)商業(yè)邏輯bean的一個(gè)合適的方法來(lái)執(zhí)行!
更新將用來(lái)創(chuàng)建下一個(gè)用戶(hù)界面頁(yè)面的服務(wù)器端對(duì)象(典型情況下是request范圍或session范圍beans,定義你需要在多長(zhǎng)時(shí)間內(nèi)保持這些項(xiàng)目可獲得)!
返回一個(gè)標(biāo)識(shí)生成響應(yīng)的JSP頁(yè)面的適當(dāng)?shù)摹ctionForward 對(duì)象,基于新近更新的beans。典型情況下,你將通過(guò)在你接收到的 ActionMapping 對(duì)象(如果你使用一個(gè)局部于與這個(gè)映射上的邏輯名)或者在controller servlet 本身(如果你使用一個(gè)全局于應(yīng)用程序的邏輯名)上調(diào)用 findForward() 得到一個(gè)對(duì)這樣一個(gè)對(duì)象的引用!
當(dāng)為 Action 類(lèi)編程時(shí)要記住的設(shè)計(jì)要點(diǎn)包括以下這些: 
controller servlet僅僅創(chuàng)建一個(gè)你的 Action 類(lèi)的實(shí)例,用于所有的請(qǐng)求。這樣你需要編寫(xiě)你的 Action 類(lèi)使其能夠在一個(gè)多線程環(huán)境中正確運(yùn)行,就象你必須安全地編寫(xiě)一個(gè)servlet的 service() 方法一樣!
幫助線程安全編程的最重要的原則就是在你的 Action 類(lèi)中僅僅使用局部變量而不是實(shí)例變量。局部變量創(chuàng)建于一個(gè)分配給(由你的JVM)每個(gè)請(qǐng)求線程的棧中,所以沒(méi)有必要擔(dān)心會(huì)共享它們!
盡管不應(yīng)該,代表你的系統(tǒng)中Model部分的的beans仍有可能拋出違例。你應(yīng)該在你的 perform() 方法的邏輯中捕捉所有這樣的違例,并且通過(guò)執(zhí)行以下語(yǔ)句將它們記錄在應(yīng)用程序的日志文件中(包括相應(yīng)的棧跟蹤信息): 
servlet.log("Error message text", exception);
作為一個(gè)通用的規(guī)則,分配很少的資源并在來(lái)自同一個(gè)用戶(hù)(在用戶(hù)的session中)的請(qǐng)求間保持它們會(huì)導(dǎo)致可伸縮性的問(wèn)題。你應(yīng)該在將控制重定向到適當(dāng)?shù)腣iew組件前努力釋放這樣的資源(例如數(shù)據(jù)庫(kù)聯(lián)接) -- 甚至在你調(diào)用的一個(gè)bean拋出了一個(gè)違例時(shí)!
另外,你將會(huì)想要防止出現(xiàn)非常大的 Action 類(lèi)。最簡(jiǎn)單的實(shí)現(xiàn)途徑是將你的功能邏輯嵌入到 Action 類(lèi)本身,而不是將其寫(xiě)在獨(dú)立的商業(yè)邏輯beans中。除了使 Action 類(lèi)難于理解和維護(hù)外,這種方法也使得難于重用這些商業(yè)邏輯代碼,因?yàn)榇a被嵌入到一個(gè)組件(Action 類(lèi))中并被捆綁運(yùn)行于web應(yīng)用程序環(huán)境中!
包括在Struts中的例子程序某種程度上延伸了這個(gè)設(shè)計(jì)原則,因?yàn)樯虡I(yè)邏輯本身是嵌入到 Action 類(lèi)中的。這應(yīng)該被看作是在這個(gè)樣本應(yīng)用程序設(shè)計(jì)中的一個(gè)bug,而不是一個(gè)Struts體系結(jié)構(gòu)中的固有特性,或者是一個(gè)值得仿效的方法。 

4.3 ActionMapping實(shí)現(xiàn)

為了成功地運(yùn)行,Struts的controller servlet需要知道關(guān)于每個(gè)URI該怎樣映射到一個(gè)適當(dāng)?shù)摹ction 類(lèi)的幾件事。需要了解的知識(shí)封裝在一個(gè)叫做 ActionMapping 的Java接口中,它有以下屬性: 
actionClass - 用于這個(gè)映射的 Action 類(lèi)完整的Java類(lèi)名。第一次一個(gè)特定的映射被使用,一個(gè)這個(gè)類(lèi)的實(shí)例將被創(chuàng)建并為以后重用而保存!
formAttribute - session范圍的bean的名字,當(dāng)前的這個(gè)映射的 ActionForm 被保存在這個(gè)bean之下。如果這個(gè)屬性沒(méi)有被定義,沒(méi)有 ActionForm 被使用!
formClass - 用于這個(gè)映射的 ActionForm 類(lèi)完整的Java類(lèi)名。如果你在使用對(duì)form beans的支持,這個(gè)類(lèi)的一個(gè)實(shí)例將被創(chuàng)建并保存(在當(dāng)前的用戶(hù)會(huì)話中) 
path - 匹配選擇這個(gè)映射的請(qǐng)求的URI路徑?聪旅嫒绾纹ヅ涞睦!
Struts在一個(gè)叫做 ActionMappingBase 的類(lèi)中包括了一個(gè) ActionMapping 接口的方便的實(shí)現(xiàn)。如果你不需要為你自己的映射定義任何附加的屬性,盡管把這個(gè)類(lèi)作為你的 ActionMapping 類(lèi)好了,就向下面部分描述的那樣配置。然而,定義一個(gè) ActionMapping 實(shí)現(xiàn)(多半是擴(kuò)展 ActionMappingBase 類(lèi))來(lái)包含附加的屬性也是可能的。controller servlet知道怎樣自動(dòng)配置這些定制屬性,因?yàn)樗褂肧truts的Digester模塊來(lái)讀配置文件。 
包括在Struts的例子程序中,這個(gè)特性用來(lái)定義兩個(gè)附加的屬性: 
failure - 如果Action類(lèi)檢測(cè)到它接收的輸入字段的一些問(wèn)題,控制應(yīng)該被重定向到的上下文相關(guān)的URI。典型情況下是請(qǐng)求發(fā)向的JSP頁(yè)面名,它將引起表單被重新顯示(包含Action類(lèi)設(shè)置的出錯(cuò)消息和大部分最近的來(lái)自ActionForm bean的輸入值)!
success - 如果Action類(lèi)成功執(zhí)行請(qǐng)求的功能,控制應(yīng)該被重定向到的上下文相關(guān)的URI。典型情況下是準(zhǔn)備這個(gè)應(yīng)用程序的會(huì)話流的下一個(gè)頁(yè)面的JSP頁(yè)面名!
使用這兩個(gè)額外的屬性,例子程序中的 Action 類(lèi)幾乎完全獨(dú)立于頁(yè)面設(shè)計(jì)者使用的實(shí)際的JSP頁(yè)面名。這個(gè)頁(yè)面可以在重新設(shè)計(jì)時(shí)被重命名,然而幾乎不會(huì)影響到 Action 類(lèi)本身。如果“下一個(gè)”JSP頁(yè)面的名字被硬編碼到 Action 類(lèi)中,所有的這些類(lèi)也需要被修改!

4.4 Action映射配置文件

controller servlet怎樣知道你想要得到的映射?寫(xiě)一個(gè)簡(jiǎn)單地初始化新的 ActionMapping 實(shí)例并且調(diào)用所有適當(dāng)?shù)膕et方法的小的Java類(lèi)是可能的(但是很麻煩)。為了使這個(gè)處理簡(jiǎn)單些,Struts包括一個(gè)Digester模塊能夠處理一個(gè)想得到的映射的基于XML的描述,同時(shí)創(chuàng)建適當(dāng)?shù)膶?duì)象?础PI 文檔 以獲得關(guān)于Digester更多的信息!
開(kāi)發(fā)者的責(zé)任是創(chuàng)建一個(gè)叫做 action.xml 的XML文件,并且把它放在你的應(yīng)用程序的WEB-INF目錄中。(注意這個(gè)文件并不需要 DTD,因?yàn)閷?shí)際使用的屬性對(duì)于不同的用戶(hù)可以是不同的)最外面的XML元素必須是<action-mappings>,在這個(gè)元素之中是嵌入的0個(gè)或更多的 <action> 元素 -- 每一個(gè)對(duì)應(yīng)于你希望定義的一個(gè)映射。 

來(lái)自例子程序的 action.xml 文件包括“注冊(cè)”功能的以下映射條目,我們用來(lái)說(shuō)明這個(gè)需求: 

<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>
就象你所看到的,這個(gè)映射匹配路徑 /logon。▽(shí)際上,因?yàn)槔映绦蚴褂脭U(kuò)展匹配,你在一個(gè)JSP頁(yè)面指定的請(qǐng)求的URI結(jié)束于/logon.do)。當(dāng)接收到一個(gè)匹配這個(gè)路徑的請(qǐng)求時(shí),一個(gè) LogonAction 類(lèi)的實(shí)例將被創(chuàng)建(僅僅在第一次)并被使用。controller servlet將在關(guān)鍵字 logonForm 下查找一個(gè)session范圍的bean,如果需要就為指定的類(lèi)創(chuàng)建并保存一個(gè)bean。 
這個(gè) action 元素也定義了一個(gè)邏輯名“success”,它在 LogonAction 類(lèi)中被用來(lái)標(biāo)識(shí)當(dāng)一個(gè)用戶(hù)成功注冊(cè)時(shí)使用的頁(yè)面。象這樣使用一個(gè)邏輯名允許將 action 類(lèi)隔離于任何由于重新設(shè)計(jì)位置而可能發(fā)生的頁(yè)面名改變。 

這是第二個(gè)在任何 action 之外宣告的 forward 元素,這樣它就可以被所有的action全局地獲得。在這個(gè)情況下,它為注冊(cè)頁(yè)面定義了一個(gè)邏輯名。當(dāng)你調(diào)用 mapping.findForward() 時(shí)在你的 action 代碼中,Struts首先查找這個(gè)action本地定義的邏輯名。如果沒(méi)有找到,Struts會(huì)自動(dòng)為你查找全局定義的邏輯名。 

4.5 Web應(yīng)用程序展開(kāi)描述符

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

4.5.1 配置Action Servlet實(shí)例

添加一個(gè)條目定義action servlet本身,同時(shí)包括適當(dāng)?shù)某跏蓟瘏?shù)。這樣一個(gè)條目看起來(lái)象是這樣: 
<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支持的初始化參數(shù)在下面描述,拷貝自 ActionServlet 類(lèi)的 Javadocs 。方括號(hào)描述如果你沒(méi)有為那個(gè)初始化參數(shù)提供一個(gè)值時(shí)假設(shè)的缺省值!
application - 應(yīng)用程序資源包基類(lèi)的Java類(lèi)名。[NONE]. 
config - 包含配置信息的XML資源的上下文相關(guān)的路徑。[/WEB-INF/action.xml] 
debug - 這個(gè)servlet的調(diào)試級(jí)別,它控制記錄多少信息到日志中。[0] 
digester - 我們?cè)凇nitMapping() 中利用的Digester的調(diào)試級(jí)別,它記錄到System.out而不是servlet的日志中。[0] 
forward - 使用的ActionForward實(shí)現(xiàn)的Java類(lèi)名。[org.apache.struts.action.ActionForward] 
mapping - 使用的ActionMapping實(shí)現(xiàn)的Java類(lèi)名。[org.apache.struts.action.ActionMappingBase] 
nocache - 如果設(shè)置為 true,增加HTTP頭信息到所有響應(yīng)中使瀏覽器對(duì)于生成或重定向到的任何響應(yīng)不做緩沖。[false] 
null - 如果設(shè)置為 true,設(shè)置應(yīng)用程序資源使得如果未知的消息關(guān)鍵字被使用則返回 null。否則,一個(gè)包括不歡迎的消息關(guān)鍵字的出錯(cuò)消息將被返回。[true] 

4.5.2 配置Action Servlet映射

有兩種通常的方法來(lái)定義將被controller servlet處理的URL -- 前綴匹配和擴(kuò)展匹配。每種方法的一個(gè)適當(dāng)?shù)挠成錀l目將在下面被描述!
前綴匹配意思是你想讓所有以一個(gè)特殊值開(kāi)頭(在上下文路徑部分之后)的URL傳遞給這個(gè)servlet。這樣一個(gè)條目看起來(lái)可以象是這樣: 

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
它意味著一個(gè)匹配前面描述的 /logon 路徑的請(qǐng)求的URL看起來(lái)象是這樣: 
http://www.mycompany.com/myapplication/execute/logon
這里 /myapplicationis 是你的應(yīng)用程序展開(kāi)所在的上下文路徑!
另一方面,擴(kuò)展映射基于URL以一個(gè)跟著定義的一組字符的句點(diǎn)結(jié)束的事實(shí)而將URL匹配到action servlet 。例如,JSP處理servlet映射到 *.jsp 模式這樣它在每個(gè)JSP頁(yè)面請(qǐng)求時(shí)被調(diào)用。為了使用 *.do 擴(kuò)展(它意味著“做某件事”)映射條目看起來(lái)應(yīng)該象是這樣: 

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
并且一個(gè)匹配以前描述的 /logon 路徑的請(qǐng)求的URI可以看起來(lái)象是這樣: 
http://www.mycompany.com/myapplication/logon.do

4.5.3 配置Struts標(biāo)記庫(kù)

下一步,你必須添加一個(gè)定義Struts標(biāo)記庫(kù)的條目。這個(gè)條目看起來(lái)應(yīng)該象是這樣: 
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>
它告訴JSP系統(tǒng)到哪里去找這個(gè)庫(kù)的標(biāo)記庫(kù)描述符(在你的應(yīng)用程序的WEB-INF目錄,而不是在外部互聯(lián)網(wǎng)上的某個(gè)地方)。 

4.5.4 添加Struts組件到你的應(yīng)用程序中

為了在你的應(yīng)用程序運(yùn)行時(shí)使用Struts,你必須將 struts.tld 文件拷貝到你的 WEB-INF 目錄,將 struts.jar 文件拷貝到你的 WEB-INF/lib !? 



(2)
了解MVC

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

Struts1.1的新功能
Struts1.1與1.0相比加了一些很不錯(cuò)的功能。最主要是表單驗(yàn)證上功能增強(qiáng)。在Struts1.1
數(shù)據(jù)的驗(yàn)證不象以前在Action中在validator具體實(shí)現(xiàn),而是在validation.xml通過(guò)配置實(shí)現(xiàn)
這樣做的好處就是重用性加強(qiáng)了很多。

Struts1.1實(shí)現(xiàn)的主要組成
主要包括:Action,ActionForm,ActionMapping,ActionForward,開(kāi)發(fā)當(dāng)中最主要寫(xiě)的是Action
ActionForm根據(jù)需要可以寫(xiě)或不寫(xiě)。下面我就一一具體介紹。
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開(kāi)發(fā)小組對(duì)Action的描述,說(shuō)Action實(shí)際上是Request和Business Logic
中間的適配器.通俗的說(shuō)就是從表單中取到數(shù)據(jù)并穿給商業(yè)邏輯操作進(jìn)行一系列的操作
然后返回相應(yīng)的操作信息。

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實(shí)際上就是把從Request取到的數(shù)據(jù)封裝并進(jìn)行校驗(yàn),然后把合法的數(shù)據(jù)給
Action進(jìn)行處理。實(shí)際上ActionForm除了進(jìn)行數(shù)據(jù)校驗(yàn)之外另外更重要的是在表單回寫(xiě)
的時(shí)候作用很大。反而在1.1以后數(shù)據(jù)校驗(yàn)的大部分工作在validation.xml去實(shí)現(xiàn)。

ActionMapping,ActionForward
ActionMapping主要是用與配置和描述相關(guān)屬性使用的。先看下在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就是用來(lái)描述一個(gè)Action的URL、具體實(shí)現(xiàn)的文件、相對(duì)應(yīng)的ActionForm
數(shù)據(jù)屬性(request or session)、是否需要進(jìn)行數(shù)據(jù)校驗(yàn)和回寫(xiě)、以及處理完成后可能
跳轉(zhuǎn)的URL.
而ActionForward你就可以理解為Action 操作完成后的跳轉(zhuǎn)URL,Action在處理完相關(guān)操作后
返回的是一個(gè)ActionForward也就是告訴Struts我做完這個(gè)操作下一步到哪兒去。


構(gòu)建Struts1.1運(yùn)行環(huán)境
我的配置是居于Tomcat4.0以上版本討論,其他的AppServer大致相同。
1:得到Struts1.1
http://jakarta.apache.org/builds/jakarta-struts/release/v1.1-b1/jakarta-struts-1.1-b1.zip
2:設(shè)置
把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中提供了很詳細(xì)的例子,你可以仔細(xì)看看.
接下來(lái)你該根據(jù)需要配置struts-config.xml,以下是一個(gè)簡(jiǎn)單的例子
<?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>
上面的英文我相信你能夠看懂。我就不做解釋了。你需要繼續(xù)配置validation.xml了,看如下
簡(jiǎn)單的例子.
<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取到的數(shù)據(jù)應(yīng)該做下面的驗(yàn)證
username是必須不能為空的并且最小長(zhǎng)度為5,最大長(zhǎng)度是20.
................
password,nickname,superpass基本一樣我就不做更多說(shuō)明.至次配置基本結(jié)束,我們要開(kāi)始寫(xiě)
第一個(gè)Struts 了。

開(kāi)發(fā)Struts1.1

usereg.jsp
為了考慮頁(yè)面的靈活性,在頁(yè)面中顯示的所有元素我都放在properties文件中并由
com.bingo.finance.common.HtmlMsg這個(gè)文件取到.
===================================================================
<%@ 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中默認(rèn)的后綴是.do你可以根據(jù)需要自己修改-->
<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%">
<!--為了實(shí)現(xiàn)回寫(xiě)在Struts中建議你使用Tag使用如下寫(xiě)法代替一般的寫(xiě)法
<html:text property="username" size="20" maxlength="30" styleClass="css" styleId="userId"/>
上面的代碼你看不懂你需要進(jìn)一步了解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>

</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
//實(shí)際上ValidatorForm是繼承ActionForm
//為什么要加一層ValidatorForm是為了校驗(yàn)而做的
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文件我就不提供了,這一部分在實(shí)際的開(kāi)發(fā)當(dāng)中根據(jù)需要自己去
處理,也就是為了把數(shù)據(jù)插入數(shù)據(jù)庫(kù)。
現(xiàn)在一個(gè)簡(jiǎn)單的注冊(cè)用戶(hù)你就開(kāi)發(fā)完成了。很簡(jiǎn)單吧。呵呵,繼續(xù)努力...但其中還有很多
細(xì)節(jié)你需要進(jìn)一步了解,我只把你領(lǐng)到門(mén)了,你必須自己去研究一些東西,比如說(shuō),我希望
我的用戶(hù)多一個(gè)Email字段和一個(gè)年齡字段我應(yīng)該如何做,而且我希望validation.xml的驗(yàn)證
有Email合法驗(yàn)證,年齡必須大于0而且一定是整數(shù)。那我應(yīng)該如何做?
我只能告訴你加兩個(gè)字段你需要修改Form 增加字段,同時(shí)相應(yīng)文件也要修改。在struts中
Email的驗(yàn)證非常簡(jiǎn)單。好好看例子吧,好好研究validator-rules.xml,這個(gè)我認(rèn)為struts1.1
最好的功能增加(Struts1.0沒(méi)有這個(gè)文件)。Struts中提供了一個(gè)非常靈活而且重用極高的驗(yàn)證機(jī)制。

Struts和其他開(kāi)發(fā)方式的比較
使用Struts開(kāi)發(fā)至少帶來(lái)如下好處:
1:層次結(jié)構(gòu)非常清晰,也使得分工很明確。
2:重用度很高,連數(shù)據(jù)驗(yàn)證都可以重用,還有一個(gè)更好的重用就是Form,Action是可以很好的重用的。
3:可維護(hù)性好。這是居于第一點(diǎn)的。
.................
下面我說(shuō)一個(gè)Struts的不足之處,如果你使用或者了解TDK你就會(huì)覺(jué)得,TDK中可以自動(dòng)生成很多
java源代碼的確可以節(jié)省不少工作量,Struts中也可以做的很好。我自己開(kāi)發(fā)了一個(gè)工具,功能
還不夠強(qiáng)大。在我的計(jì)劃當(dāng)中,我希望Form,JavaBean(封裝數(shù)據(jù)相當(dāng)于EJB中的Entity Bean),
甚至包括操作數(shù)據(jù)庫(kù)的Method都自動(dòng)生成,還有validation.xml也可以動(dòng)態(tài)的生成。然而因?yàn)?br>下一個(gè)原因:Struts到目前為止還不是一個(gè)比較成熟的項(xiàng)目。他的改動(dòng)實(shí)在是太大了。1.0和0.5
就有很大的改動(dòng)。所以我的計(jì)劃有變。我希望在1.1完全推出來(lái)再做開(kāi)發(fā)(1.1現(xiàn)在是Beta版)。
同時(shí)我也給Craig R. McClanahan(Struts開(kāi)發(fā)的Team leader)寫(xiě)過(guò)信聽(tīng)取他的建議.
-完-


標(biāo)簽:Struts1.1 介紹