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

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

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

    程序 人生

    程序 人生

    BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
      11 Posts :: 2 Stories :: 18 Comments :: 0 Trackbacks

    ? Struts快速學(xué)習(xí)指南 素材來(lái)自于《Programming Jakarta Struts》一書??
    1.???? Struts簡(jiǎn)介
    Struts 是一個(gè)技術(shù)框架,由Craig R. McClanahan編寫,并且在2000年的時(shí)候捐獻(xiàn)給了ASF,目前,有很多組織和個(gè)人參與Struts框架的開發(fā),使得Struts保持高速成長(zhǎng),同時(shí),利用Struts開發(fā)的應(yīng)用越來(lái)越多,使其成為web應(yīng)用MVC模式中VC部分事實(shí)上的標(biāo)準(zhǔn)。
    1.1? Web技術(shù)歷史
    1.1.1?????????? CGI
    web應(yīng)用開發(fā)中歷史上,CGI(common gateway interface)是最早使用的一種技術(shù),通過為不同的平臺(tái),不同的web server編寫插件編寫應(yīng)用接口,來(lái)滿足通過web方式編寫應(yīng)用的需求。當(dāng)時(shí)流行的方式包含NSAPI/ISAPI,使用Perl來(lái)編寫CGI程序。CGI最大的問題就是線程并發(fā)的問題,當(dāng)時(shí)給很多人的感覺是CGI訪問速度慢,其主要原因是應(yīng)用程序所編寫的CGI沒有考慮多線程。
    1.1.2?????????? Servlet
    ??? 作為一種跨平臺(tái)語(yǔ)言的服務(wù)器端技術(shù),其一經(jīng)產(chǎn)生就備受矚目,采用Servlet開發(fā)的應(yīng)用,不用考慮平臺(tái),多線程等讓人頭疼的問題,使得開發(fā)人員專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),大大解放了生產(chǎn)力。但是,在Servlet中嵌入html無(wú)疑是開發(fā)人員的噩夢(mèng),與同時(shí)期微軟的ASP相比,Servlet在開發(fā)效率方面讓人不敢恭維。
    1.1.3?????????? Java Server Pages
    JSP從很大程度上時(shí)參考了ASP的想法,使得采用Java語(yǔ)言開發(fā)服務(wù)器端應(yīng)用非常容易,同時(shí)因?yàn)閖ava與生俱來(lái)的跨平臺(tái)、安全性、易用性優(yōu)勢(shì),當(dāng)然,還有開發(fā)人員的高工資 ,使得JSP逐漸在Web服務(wù)器端應(yīng)用開發(fā)中占據(jù)了主流位置。
    ?
    2.???? Struts安裝
    Struts作為一個(gè)J2EE 框架,很容易和你的web應(yīng)用結(jié)合起來(lái),你僅僅需要作以下幾個(gè)步驟:
    1、? 下在Struts1.1 二進(jìn)制壓縮包,將壓縮包解壓到%STRUTS_HOME%目錄,目錄結(jié)構(gòu)如下如示:
    ?
    2、? 建立你的標(biāo)準(zhǔn)web應(yīng)用程序,所謂標(biāo)準(zhǔn)應(yīng)用程序是指在web應(yīng)用程序的根目錄下有一個(gè)WEB-INFO目錄,WEB-INF下有classes,lib目錄,classes下面有個(gè)web.xml文件。本文后續(xù)假設(shè)你的web應(yīng)用在%WEB_ROOT%目錄下。
    3、? 將%STRUTS_HOME%/lib下所有文件copy到%WEB_ROOT%/WEB-INF/lib下。
    4、? 配置%WEB_ROOT%/WEB-INF/classes/web.xml以滿足Struts需要,具體如下:
    1、? 在配置文件中映射ActionServlet,ActionServlet用于接受所有訪問者的請(qǐng)求。在Struts應(yīng)用中,所有對(duì)應(yīng)用程序的請(qǐng)求,都會(huì)被WEB SERVER定向到ActionServlet進(jìn)行統(tǒng)一控制、分配處理,ActionServlet可以看作是Struts框架的核心,樞紐。
    ?? <web-app>?
    ?<servlet>
    ? <servlet-name>controller</servlet-name>
    ? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    ?</servlet>
    </web-app>
    ?
    2、? 配置servlet映射,通過servlet映射可以將用戶訪問web應(yīng)用的擴(kuò)展名映射到具體處理的servlet,例如,將所有以.do為擴(kuò)展名的頁(yè)面的請(qǐng)求交給ActionServlet處理。
    <web-app>?
    ?<servlet>
    ? <servlet-name>controller</servlet-name>
    ? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    ?</servlet>
    ?<servlet-mapping>
    ? <servlet-name>controller</servlet-name>
    ? <url-pattern>*.do</url-pattern>
    ?</servlet-mapping>
    </web-app>
    ? 另外,也可以采用如下方式進(jìn)行映射,該方式將所有對(duì)/action/目錄下文件的訪問請(qǐng)求交給ActionServlet處理。
    <web-app>?
    ?<servlet>
    ? <servlet-name> controller </servlet-name>
    ? <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    ?</servlet>
    ?<servlet-mapping>
    ? <servlet-name>controller</servlet-name>
    ? <url-pattern>>/action/*</url-pattern>
    ?</servlet-mapping>
    </web-app>
    ?
    3、? 配置ActionServlet的初始化參數(shù),Struts1.1有一些指定的初始化參數(shù),用于指明Struts應(yīng)用所需要的配置文件,debug等級(jí)等。
    <web-app>?
    ?<servlet>
    ? <servlet-name>controller</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>host</param-name>
    ?? <param-value>localhost</param-value>
    ? </init-param>
    ? <init-param>
    ?? <param-name>port</param-name>
    ?? <param-value>7001</param-value>
    ? </init-param>
    ?</servlet>
    ?<servlet-mapping>
    ? <servlet-name> controller </servlet-name>
    ? <url-pattern>*.do</url-pattern>
    ?</servlet-mapping>
    </web-app>
    初始化參數(shù)利用<init-param>進(jìn)行配置,配置采用名稱-值對(duì)的方式,一個(gè)<param-name>對(duì)應(yīng)一個(gè)<param-value>,初始化參數(shù)可以任意定義,例如host,port,但是有一些在Struts1.1中是具有特別意義的,列舉如下:
    表2-1. Struts1.1中用到的初始化參數(shù)
    參數(shù)名?含義/默認(rèn)值
    config ?以相對(duì)路徑的方式指明Struts應(yīng)用程序的配置文件位置。如不設(shè)置,則默認(rèn)值為/WEB-INF/struts-config.xml。
    config/sub1 ?以相對(duì)路徑的方式指明子應(yīng)用程序的配置文件位置,一般來(lái)說(shuō),很少用到子應(yīng)用程序,在此不多描述。
    debug ?設(shè)置Servlet的debug級(jí)別,控制日志記錄的詳細(xì)程度。默認(rèn)為0,記錄相對(duì)最少的日志信息。
    detail ?設(shè)置Digester的debug級(jí)別,Digester是Struts框架所使用的用來(lái)解析xml配置文件的一個(gè)框架,通過該設(shè)置,可以查看不同詳細(xì)等級(jí)的解析日志。默認(rèn)為0,記錄相對(duì)最少的日志信息。

    4、? 配置標(biāo)簽庫(kù),標(biāo)簽庫(kù)是Struts自帶的一些組件庫(kù),采用JSP規(guī)范中Tag-lib的方式供大家使用,正是因?yàn)榇嬖谶@么豐富的標(biāo)簽庫(kù),使得采用Struts的開發(fā)才顯得這么方便,高效。

    ? <web-app>
    ?<servlet>
    ? <servlet-name>controller</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>host</param-name>
    ?? <param-value>localhost</param-value>
    ? </init-param>
    ? <init-param>
    ?? <param-name>port</param-name>
    ?? <param-value>7001</param-value>
    ? </init-param>
    ?</servlet>
    ?
    ?<servlet-mapping>
    ? <servlet-name>controller</servlet-name>
    ? <url-pattern>*.do</url-pattern>
    ?</servlet-mapping>
    ?
    ?<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-bean.tld</taglib-uri>
    ? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    ?</taglib>
    ?
    ?<taglib>
    ? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    ? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
    ?</taglib>
    </web-app>
    標(biāo)簽庫(kù)采用<taglib>定義,<taglib>含有兩個(gè)子元素,<taglib-uri>和<taglib-location>,<taglib-uri>用戶定義標(biāo)簽庫(kù)的唯一表示符,可以理解為名字,以后要在jsp頁(yè)面中使用這個(gè)標(biāo)簽庫(kù),靠的就是它。<taglib-location>指明標(biāo)簽庫(kù)存在的物理路徑,當(dāng)然,和配置文件一樣,也是相對(duì)路徑。
    5、? 設(shè)置welcome文件列表(可選步驟)
    <welcome-file-list>
    ?<welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    6、? 設(shè)置錯(cuò)誤處理(可選步驟),通常的http訪問異常包含404 Not Found和500 Internal Error,為了提供給用戶更為友好的顯示,可以做如下配置:
    <web-app>
    ?<error-page>
    ? <error-code>404</error-code>
    ? <location>/common/404.jsp</location>
    ?</error-page>
    ?
    ?<error-page>
    ? <error-code>500</error-code>
    ? <location>/common/500.jsp</location>
    ?</error-page>
    </web-app>
    通過如上配置,當(dāng)用戶訪問應(yīng)用中不存在的頁(yè)面時(shí),將會(huì)將用戶導(dǎo)向到/common/404.jsp頁(yè)面。同樣地,當(dāng)出現(xiàn)異常錯(cuò)誤時(shí),將會(huì)把/common/500.jsp顯示給用戶。
    7、? 最后,一個(gè)完整的web.xml示例如下:
    <?xml version="1.0" encoding="UTF-8"?>
    ?
    <!DOCTYPE web-app
    ??? PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    ??? "<web-app>?
    ?<servlet>
    ? <servlet-name>storefront</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>1</load-on-startup>
    ?</servlet>
    ?
    ?<servlet-mapping>
    ? <servlet-name>storefront</servlet-name>
    ? <url-pattern>/action/*</url-pattern>
    ?</servlet-mapping>
    ?
    ?<welcome-file-list>
    ? <welcome-file>index.jsp</welcome-file>??
    ?</welcome-file-list>
    ?
    ?<error-page>
    ? <error-code>404</error-code>
    ? <location>/common/404.jsp</location>
    ?</error-page>
    ?<error-page>
    ? <error-code>500</error-code>
    ? <location>/common/500.jsp</location>
    ?</error-page>
    ?
    ?<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-bean.tld</taglib-uri>
    ? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    ?</taglib>
    ?<taglib>
    ? <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    ? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
    ?</taglib>
    </web-app>
    1、? 到此為止,Struts的開發(fā)環(huán)境安裝算是告一段落。
    1.???? Struts框架
    在介紹Struts框架之前,先來(lái)看看web開發(fā)的兩種模式,這兩種模式自JSP開發(fā)流行以來(lái),就爭(zhēng)論不斷,它們分別是JSP Model 1和JSP Model 2。
    1.1? JSP Model 1
    下圖是JSP Model 1 的構(gòu)架示意圖:
    ?
    用戶通過瀏覽器之間訪問web應(yīng)用的JSP頁(yè)面, JSP提供UI顯示,JavaBeans處理數(shù)據(jù)庫(kù)訪問和業(yè)務(wù)邏輯。這種開發(fā)方式最大的優(yōu)勢(shì)是直接、簡(jiǎn)單,對(duì)于小型應(yīng)用,可以很方便、快速地進(jìn)行開發(fā)。
    1.2? JSP Model 2
    下圖是JSP Model 2 的構(gòu)架示意圖:
    ?
    JSP Model 2 和JSP Model 1 最大的區(qū)別是引入了MVC模式的概念,即M(Model:業(yè)務(wù)邏輯),V(View:系統(tǒng)UI),C(Controller:控制)分離,用戶的所有請(qǐng)求提交給Controller,由Controller進(jìn)行統(tǒng)一分配,并且采用推的方式將不同的UI顯示給用戶。這樣做得好處是:
    1、? 可以統(tǒng)一控制用戶的行為,例如在Controller中添加統(tǒng)一日志記錄等功能是非常方便的。
    2、? 職責(zé)分離,有利于各部分的維護(hù)。用戶不直接訪問分散的UI,這樣可以通過配置文件或則流程定義的方式,在不同的環(huán)節(jié)、時(shí)間將不同的頁(yè)面推向給用戶。
    ?
    1.3? Struts
    通過了解JSP Model 1和JSP Model 2,我想大家心里都已經(jīng)有了選擇,在這里,我不想說(shuō)哪一種構(gòu)架更好,在不同的環(huán)境中,使用恰到好處的技術(shù)才是最好的。普遍來(lái)說(shuō),MVC分離是個(gè)不錯(cuò)的選擇。
    Struts框架正是MVC分離的一個(gè)杰出作品。首先我們來(lái)看一下Struts1.1的UML圖,以便于我們對(duì)Struts有個(gè)全局的了解:
    ?
    先不用急著看懂這張圖,在下面的學(xué)習(xí)過程中,我們會(huì)慢慢地了解這張圖中各個(gè)組件的含義。
    接下來(lái),我們從MVC的角度對(duì)Struts框架進(jìn)行探索。
    1.3.1?????????? Controller
    首先介紹MVC中的C,上面提到了,JSP Model 1 和JSP Model 2 最大的卻別就是C,那么在Struts中,這個(gè)C是什么呢?他是如何實(shí)現(xiàn)的呢?下面我們?cè)賮?lái)看看這個(gè)圖:
    ?這是JSP Model 2的構(gòu)架圖,也是Struts的構(gòu)架圖,Struts使用一個(gè)Servlet作為Controller,處理用戶的請(qǐng)求,并分派給Model進(jìn)行業(yè)務(wù)處理,在合適的時(shí)候?qū)⒑线m的View推向給用戶。這個(gè)Servlet是org.apache.struts.action.ActionServlet或其子類。ActionServlet類擴(kuò)展自javax.servlet.http.HttpServlet類,其職責(zé)是將http請(qǐng)求提交給合適的處理器(Processor)進(jìn)行處理。關(guān)于處理器我們?cè)谏院髸?huì)介紹,是org.apache.struts.action.RequestProcessor 或其子類的一個(gè)實(shí)例。
    1.3.1.1??? Controller(控制器)機(jī)制
    J2EE的前端控制器(Front Controller)設(shè)計(jì)模式中利用一個(gè)前端控制器來(lái)接受所有客戶請(qǐng)求,為應(yīng)用提供一個(gè)中心控制點(diǎn),在該控制點(diǎn)上,可以很方便地添加一些全局性的,如加密、國(guó)際化、日志等通用操作。Controller的實(shí)現(xiàn)機(jī)制正是建立在前端控制器的設(shè)計(jì)模式基礎(chǔ)上。
    前面我們介紹過,Struts的控制器擁有一些職責(zé),其中最主要的是以下幾個(gè):
    ??????????????? 接收客戶請(qǐng)求。
    ??????????????? 映射請(qǐng)求到指定的業(yè)務(wù)操作。
    ??????????????? 獲取業(yè)務(wù)操作的結(jié)果并以有效的方式提供給客戶。
    ??????????????? 根據(jù)業(yè)務(wù)操作的結(jié)果和當(dāng)前的狀態(tài)把不同的? 推向給客戶。
    在Struts框架中,控制器中不同的組件負(fù)責(zé)不同的控制職責(zé),下圖是Struts框架中關(guān)于控制器部分的一個(gè)組件圖:

    ?在上圖中,很明顯地可以看出,ActionServlet處于核心位置,那么,我們就先來(lái)了解一下ActionServlet。
    1.3.1.2??? ActionServlet類
    org.apache.struts.action.ActionServlet在????? 應(yīng)用程序中扮演接收器的角色,所有客戶端的請(qǐng)求在被其它類處理之前都得通過ActionServlet 的控制。
    當(dāng) ActionServlet的實(shí)例接收到一個(gè)??? 請(qǐng)求,不管是通過?? 方法或??? 方法,ActionServlet的process( )方法被調(diào)用并用以處理客戶請(qǐng)求。process( )方法實(shí)現(xiàn)顯示如下:
    protected void process(HttpServletRequest request,HttpServletResponse response)
    ? throws IOException, ServletException {
    ?
    ? RequestUtils.selectApplication( request, getServletContext(? ) );
    ? getApplicationConfig( request ).getProcessor(? ).process( request, response );
    }
    該方法的實(shí)現(xiàn)很簡(jiǎn)單,RequestUtils.selectApplication( request, getServletContext(? ) );語(yǔ)句是用來(lái)根據(jù)用戶訪問的上下文路徑來(lái)選擇處理的應(yīng)用,如果你只有一個(gè)Struts配置文件,就表示你只有一個(gè)Struts應(yīng)用。關(guān)于如何建立多個(gè)Struts應(yīng)用,本教程不作詳細(xì)講解,請(qǐng)參考相應(yīng)資料。getApplicationConfig( request ).getProcessor(? ).process( request, response );語(yǔ)句用來(lái)獲取一個(gè)處理器,并將客戶請(qǐng)求提交給處理器處理。
    1.3.1.3??? Struts初始化處理流程
    根據(jù)在?????? 中配置的初始化參數(shù),?????? 容器將決定在在容器的第一次啟動(dòng),或第一次客戶請(qǐng)求ActionServlet的時(shí)機(jī)加載ActionServlet ,不管哪種方式加載,和其它?????? 一樣,ActionServlet的init( )方法將被調(diào)用,開始初始化過程。讓我們來(lái)看看在初始化過程中將發(fā)生些什么,理解了這些,對(duì)于我們???? 和擴(kuò)展自己的應(yīng)用更加得心應(yīng)手。
    ?????????? 初始化框架的內(nèi)部消息綁定,這些消息用來(lái)輸出提示,警告,和錯(cuò)誤信息到日志文件中。org.apache.struts.action.ActionResources用來(lái)獲取內(nèi)部消息;
    ?????????? 加載?????? 中定義的不同參數(shù),用以控制ActionServlet的不同行為,這些參數(shù)包括config? debug? detail????? convertNull ;
    ?????????? 加載并初始化?????? 中定義的??????? 名稱和?????? 映射信息。通過初始化,框架的各種?? 被注冊(cè),?? 用來(lái)在下一步校驗(yàn)配置文件的有效性;
    4、??????? 為默認(rèn)應(yīng)用加載并初始化????? 配置文件,配置文件即初始化參數(shù)config指定的文件。默認(rèn)配置文件被解析,產(chǎn)生一個(gè)ApplicationConfig對(duì)象存于ServletContext中。可以通過關(guān)鍵字org.apache.struts.action.APPLICATION從ServletContext中獲取ApplicationConfig;
    ???????????????? 配置文件中指定的每一個(gè)消息資源都被加載,初始化,并存在ServletContext的合適區(qū)域 基于每個(gè)message-resources元素的key屬性 ,如果key屬性沒有設(shè)置,則為org.apache.struts.action.MESSAGE;
    ???????????????? 配置文件中聲明的每一個(gè)數(shù)據(jù)源被加載并且初始化,如果沒有配置數(shù)據(jù)源,這一步跳過;
    ?????????? 加載并初始化????? 配置文件中指定的插件。每一個(gè)插件的init()方法被調(diào)用;
    ?????????? 當(dāng)默認(rèn)應(yīng)用加載完成,init()方法判斷是否有應(yīng)用模塊需要加載,如果有,重復(fù)步驟 - 萬(wàn)成應(yīng)用模塊的加載。
    下圖是對(duì)上面文字說(shuō)明的圖形化表示:
    ?


    RequestProcessor類
    前面提到過,當(dāng)ActionServlet接收到客戶請(qǐng)求后,會(huì)進(jìn)行一連串的初始化操作,然后,就會(huì)將客戶請(qǐng)求轉(zhuǎn)交給合適的處理器進(jìn)行處理,這個(gè)合適的處理器就是org.apache.struts.action.RequestProcessor或其子類的一個(gè)實(shí)例(根據(jù)Struts配置文件中的配置)。提供了默認(rèn)實(shí)現(xiàn),如果需要自定義這些行為,可以重載這個(gè)類定義自己的處理行為,當(dāng)你想要自定義操作時(shí),Struts推薦你重載這個(gè)類而不是ActionServlet。
    下面的代碼片斷提供了RequestProcessor的默認(rèn)行為實(shí)現(xiàn)代碼:
    public void process(HttpServletRequest request, HttpServletResponse response)
    ? throws IOException, ServletException {
    ?
    ??? // Wrap multipart requests with a special wrapper
    ??? request = processMultipart(request);
    ?
    ??? // Identify the path component we will use to select a mapping
    ??? String path = processPath(request, response);
    ??? if (path == null) {
    ????? return;
    ??? }
    ??? if (log.isInfoEnabled(? )) {
    ????? log.info("Processing a '" + request.getMethod(? ) +
    ??????? "' for path '" + path + "'");
    ??? }
    ?
    ??? // Select a Locale for the current user if requested
    ??? processLocale(request, response);
    ?
    ??? // Set the content type and no-caching headers if requested
    ??? processContent(request, response);
    ??? processNoCache(request, response);
    ?
    ??? // General-purpose preprocessing hook
    ??? if (!processPreprocess(request, response)) {
    ????? return;
    ??? }
    ?
    ??? // Identify the mapping for this request
    ??? ActionMapping mapping = processMapping(request, response, path);
    ??? if (mapping == null) {
    ????? return;
    ??? }
    ?
    ??? // Check for any role required to perform this action
    ??? if (!processRoles(request, response, mapping)) {
    ????? return;
    ??? }
    ?
    ??? // Process any ActionForm bean related to this request
    ??? ActionForm form = processActionForm(request, response, mapping);
    ??? processPopulate(request, response, form, mapping);
    ??? if (!processValidate(request, response, form, mapping)) {
    ????? return;
    ??? }
    ?
    ??? // Process a forward or include specified by this mapping
    ??? if (!processForward(request, response, mapping)) {
    ????? return;
    ??? }
    ??? if (!processInclude(request, response, mapping)) {
    ????? return;
    ??? }
    ?
    ??? // Create or acquire the Action instance to process this request
    ??? Action action = processActionCreate(request, response, mapping);
    ??? if (action == null) {
    ????? return;
    ??? }
    ?
    ??? // Call the Action instance itself
    ??? ActionForward forward =
    ????? processActionPerform(request, response, action, form, mapping);
    ?
    ??? // Process the returned ActionForward instance
    ??? processActionForward(request, response, forward);
    }

    ?


    接下來(lái),讓我們一步一步地了解process()方法到底做了什么。
    1、??????????? 調(diào)用processMultipart( )方法。如果HttpServletRequest是POST方式,且請(qǐng)求為multipart/form-data ,Struts框架將請(qǐng)求對(duì)象包裝成處理multipart 請(qǐng)求專用的請(qǐng)求對(duì)象,否則,只是簡(jiǎn)單地返回原有的請(qǐng)求對(duì)象。一般來(lái)說(shuō),除非需要處理文件上傳,否則不用關(guān)心multipart 功能的具體細(xì)節(jié)。
    2、??????????? 調(diào)用processPath( ) 方法,該方法用來(lái)從請(qǐng)求URL中獲應(yīng)用取路徑部分。獲取到的信息在稍后的步驟中用于選擇合適的Struts Action調(diào)用。
    3、??????????? 調(diào)用processLocale( ) 方法處理一些國(guó)際化的事務(wù)。
    4、?????????? 調(diào)用方法來(lái)決定processContent( )請(qǐng)求的content type編碼(encoding)方式。content type可以配合在配置文件中,也可以在jsp文件中配置,默認(rèn)為text/html。
    5、?????????? 根據(jù)noCache屬性的設(shè)置調(diào)用processNoCache( ) 方法,如果noCache設(shè)置為true。則添加合適的響應(yīng)頭到響應(yīng)對(duì)象中,使得頁(yè)面保留在瀏覽器的Cache中。這些響應(yīng)頭包含Pragma, Cache-Control, 和Expires 。
    6、??????????? 調(diào)用processPreprocess( )方法,這個(gè)方法在這兒設(shè)置一個(gè)鉤子,方法的默認(rèn)實(shí)現(xiàn)只是簡(jiǎn)單地返回true,這樣給了自定義處理器的開發(fā)者提供了一個(gè)合適的地方讓你添加自己的業(yè)務(wù)邏輯。因?yàn)檫@個(gè)方法在調(diào)用Action之前被調(diào)用,如果你重載這個(gè)方法,只需要返回false,則Action就不會(huì)被調(diào)用。例如,你可以重載這個(gè)方法用戶檢查客戶session,如果不通過就返回false。
    7、??????????? 調(diào)用processMapping( )方法,根據(jù)客戶請(qǐng)求信息中的path信息來(lái)決定是否返回ActionMapping對(duì)象實(shí)例。如果不能夠找到path的映射,則客戶將會(huì)得到一個(gè)error響應(yīng)。
    8、??????????? 通過調(diào)用processRoles( )方法檢查是否為Action配置了安全角色。如果配置了角色要求,則請(qǐng)求對(duì)象的isUserInRole( )方法被調(diào)用,如果用戶屬于這些角色,則客戶會(huì)得到顯示一個(gè)error響應(yīng)。
    9、??????????? 調(diào)用processActionForm( )方法檢查是否存在為ActionMapping配置的ActionForm 。如果存在,則在有效區(qū)域內(nèi)查找是否存在該ActionForm的實(shí)例,存在,則復(fù)用,不存在,則創(chuàng)建一個(gè)實(shí)例。然后將實(shí)例保存與再配置文件中配置好的有效區(qū)域(request,session,application)內(nèi),并用Action元素的name屬性作為該實(shí)例的關(guān)鍵字。
    10、?????? 調(diào)用processPopulate( )方法,如果存來(lái)存在為ActionMapping配置的ActionForm,則封裝請(qǐng)求對(duì)象中的數(shù)據(jù)到ActionForm中,在進(jìn)行封裝之前,先調(diào)用ActionForm的reset( )方法進(jìn)行屬性值的默認(rèn)化。
    11、???????? 調(diào)用processValidate( )方法。如果ActionForm被配置好,并且action元素的屬性validate被設(shè)置為true ,則進(jìn)一步調(diào)用validate( )方法進(jìn)行規(guī)則校驗(yàn)。如果validate( )方法校驗(yàn)失敗,就會(huì)保存一個(gè)ActionErrors對(duì)象到請(qǐng)求區(qū)域中,請(qǐng)求將會(huì)自動(dòng)重定向到action映射的input屬性所指定的頁(yè)面中。如果校驗(yàn)通過或在action映射中沒有配置ActionForm,則繼續(xù)處理請(qǐng)求。
    12、???????? 根據(jù)action映射是否配置了forward屬性或include屬性來(lái)決定下一步操作。如果配置了任意一個(gè),則相應(yīng)地調(diào)用RequestDispatcher對(duì)象的forward( )方法或include( )方法,調(diào)用后,對(duì)客戶請(qǐng)求的處理結(jié)束。否則,繼續(xù)處理請(qǐng)求。
    13、???????? 調(diào)用processActionCreate( )方法創(chuàng)建或獲取一個(gè)Action對(duì)象實(shí)例處理請(qǐng)求。processActionCreate( )方法會(huì)在緩存中查找是否存在已經(jīng)創(chuàng)建好的Action實(shí)例,如果存在,則復(fù)用,否則,則重新創(chuàng)建并將其村于緩存中。
    14、?????? 調(diào)用processActionPerform( )方法,該方法用于在一個(gè)try/catch代碼塊中調(diào)用action實(shí)例的execute( )方法,這樣確保action的execute( )方法一旦發(fā)生執(zhí)行異常能夠被RequestProcessor捕獲。
    15、?????? 調(diào)用processActionForward( )方法,并傳入action的execute( )方法所返回的ActionForward對(duì)象實(shí)例,方法通過檢查ActionForward對(duì)象實(shí)例,決定采用redirect或forword方式進(jìn)行重定向。究竟采用redirect還是forword取決于forward元素的redirect屬性值。
    擴(kuò)展RequestProcessor
    如果不想利用Struts提供的處理器,則可以擴(kuò)展它。通過兩個(gè)步驟即可實(shí)現(xiàn):
    1、? 創(chuàng)建一個(gè)新的類,該類必須是org.apache.struts.action.RequestProcessor的子類;
    2、? 在Struts配置文件中進(jìn)行聲明,例如:(粗體部分為你的自定義處理器類)
    <controller
    ? contentType="text/html;charset=UTF-8"
    ? debug="3"
    ? locale="true"
    ? nocache="true"
    ? processorClass="com.struts.framework.CustomRequestProcessor"/>

    1.1.1.1??? Action類
    如果說(shuō)ActionServlet是Struts框架的入口,RequestProcessor是消化過濾系統(tǒng),則org.apache.struts.action.Action類可以說(shuō)是整個(gè)框架的心臟。他是客戶請(qǐng)求和業(yè)務(wù)操作的連接橋,也可以將其看作是業(yè)務(wù)操作的客戶代理。
    在前面對(duì)ReqeustProcessor類的學(xué)習(xí)中,我們了解到一旦確定并得到了一個(gè)action實(shí)例,ReqeustProcessor會(huì)調(diào)用action的execute()方法處理客戶請(qǐng)求,你需要擴(kuò)展action類,并實(shí)現(xiàn)它的execute()方法,在此方法中添加你自己的處理代碼。下面給出是一個(gè)示例,這個(gè)action用來(lái)處理用戶的登錄請(qǐng)求:
    package com.oreilly.struts.storefront.security;
    ?
    import java.util.Locale;
    import javax.servlet.http.*;
    import org.apache.struts.action.*;
    import com.oreilly.struts.storefront.customer.view.UserView;
    import com.oreilly.struts.storefront.framework.exceptions.BaseException;
    import com.oreilly.struts.storefront.framework.UserContainer;
    import com.oreilly.struts.storefront.framework.StorefrontBaseAction;
    import com.oreilly.struts.storefront.framework.util.IConstants;
    import com.oreilly.struts.storefront.service.IStorefrontService;
    ?
    /**
    ?* Implements the logic to authenticate a user for the Storefront application.
    ?*/
    public class LoginAction extends StorefrontBaseAction {
    ? /**
    ?? * Called by the controller when the user attempts to log in to the
    ?? * Storefront application.
    ?? */
    ? public ActionForward execute( ActionMapping mapping,
    ??????????????????????????????? ActionForm form,
    ??????????????????????????????? HttpServletRequest request,
    ??????????????????????????????? HttpServletResponse response )
    ? throws Exception{
    ?
    ??? // The email and password should have already been validated by the ActionForm
    ??? String email = ((LoginForm)form).getEmail(? );
    ??? String password = ((LoginForm)form).getPassword(? );
    ?
    ??? // Log in through the security service
    ??? IStorefrontService serviceImpl = getStorefrontService(? );
    ??? UserView userView = serviceImpl.authenticate(email, password);
    ?
    ??? // Create a single container object to store user data
    ??? UserContainer existingContainer = null;
    ??? HttpSession session = request.getSession(false);
    ??? if ( session != null ){
    ????? existingContainer = getUserContainer(request);
    ????? session.invalidate(? );
    ??? }else{
    ????? existingContainer = new UserContainer(? );
    ??? }
    ?
    ??? // Create a new session for the user
    ??? session = request.getSession(true);
    ?
    ??? // Store the UserView in the container and store the container in the session
    ??? existingContainer.setUserView(userView);
    ??? session.setAttribute(IConstants.USER_CONTAINER_KEY, existingContainer);
    ?
    ??? // Return a Success forward
    ??? return mapping.findForward(IConstants.SUCCESS_KEY);
    ? }
    }
    ?
    1.1.1.1.1????? Action類緩沖
    Action類被設(shè)計(jì)為線程安全的,在每個(gè)應(yīng)用中每個(gè)Action類只會(huì)被實(shí)例化一次,供所有線程共享。RequestProcessor利用一個(gè)HashMap用來(lái)保存Action實(shí)例。
    思考題?
    所有線程共享一個(gè)Action類實(shí)例意味著什么?我們?cè)诰幊讨行枰⒁庑┦裁茨兀?br />?
    1.1.1.2??? ActionForward類
    從前面的介紹我們已經(jīng)了解到,Action的execute( )方法返回一個(gè)ActionForward對(duì)象。ActionForward對(duì)象是JSP頁(yè)面、Java servlet等web資源的抽象表現(xiàn)。
    ActionForward的用途是為了減少應(yīng)用和物理資源(JSP頁(yè)面,Java servlet)的耦合,物理資源只需要在配置文件中指定(利用name,path屬性和forward元素的redirect屬性),而不是在代碼中指定。RequestDispatcher利用ActionForward來(lái)執(zhí)行重定向操作。
    要在Action中返回一個(gè)ActionForward對(duì)象,你可以動(dòng)態(tài)地創(chuàng)建一個(gè)ActionForward 對(duì)象,不過更為通用的解決方案是,通過在Struts配置文件中進(jìn)行action映射,然后通過關(guān)鍵字去查找一個(gè)ActionForward 。下面是代碼示例:
    ? return mapping.findForward( "Success" );
    上面的代碼中,"Success"作為參數(shù)被傳遞到ActionMapping的findFoward( )方法中,findFoward( )方法在Struts配置文件的global-forwards區(qū)域,以及被調(diào)用的action的forward元素中查找名字和"Success"相匹配的元素。下面是action元素中的forward示例:
    <action
    ?? input="/security/signin.jsp"
    ?? name="loginForm"
    ?? path="/signin"
    ?? scope="request"
    ?? type="com.oreilly.struts.storefront.security.LoginAction"
    ?? validate="true">
    ?? <forward name="Success" path="/index.jsp" redirect="true"/>
    ?? <forward name="Failure" path="/security/signin.jsp" redirect="true"/>
    ?</action>

    1.1.1.1??? Action和業(yè)務(wù)邏輯
    思考題?
    Action屬于MVC中的Controller還是Model?為什么?
    ?
    1.1.1.2??? 使用Struts內(nèi)置的Action
    Struts1.1框架的org.apache.struts.actions包中包含了5個(gè)內(nèi)置的Action,用來(lái)執(zhí)行一些通用的操作,你可以把它們用在你的項(xiàng)目中,以節(jié)省你的開發(fā)時(shí)間。接下來(lái)我們分別介紹這5個(gè)內(nèi)置的Action。
    1.1.1.2.1????? org.apache.struts.actions.ForwardAction類
    很多情況下,你僅僅需要引導(dǎo)客戶從一個(gè)JSP頁(yè)面跳轉(zhuǎn)到另外一個(gè)JSP頁(yè)面,按照我們通常的做法,可以做一個(gè)鏈接讓用戶直接訪問要跳轉(zhuǎn)到的頁(yè)面。但是MVC模式不推薦你這么做,因?yàn)椋珻ontroller的職責(zé)就是接收所有的客戶請(qǐng)求,然后將客戶請(qǐng)求提交給一個(gè)合適的模塊進(jìn)行處理,并將合適的UI推給用戶,如果直接方式JSP頁(yè)面,則跳過了Controller的控制,則無(wú)法享受Controller所提供的優(yōu)點(diǎn)。為了解決這個(gè)問題,并且不用你去為了執(zhí)行一個(gè)簡(jiǎn)單的重定向操作而創(chuàng)建一個(gè)Action類 ,Struts框架提供了ForwardAction類,這個(gè)Action只是簡(jiǎn)單地執(zhí)行一個(gè)重定向操作,重定向的目的地通過parameter屬性配置。要使用ForwardAction類,只需要在Struts配置文件中將Action的type屬性配置為org.apache.struts.actions.ForwardAction:
    <action
    ? input="/index.jsp"
    ? name="loginForm"
    ? path="/viewsignin"
    ? parameter="/security/signin.jsp"
    ? scope="request"
    ? type="org.apache.struts.actions.ForwardAction"
    ? validate="false"/>
    </action>
    當(dāng)你訪問/viewsignin的時(shí)候,就會(huì)自動(dòng)重定向到/security/signin.jsp。
    1.1.1.2.2????? org.apache.struts.actions.IncludeAction類
    暫略
    1.1.1.2.3????? org.apache.struts.actions.DispatchAction類
    暫略
    1.1.1.2.4????? org.apache.struts.actions.LookupDispatchAction類
    暫略
    1.1.1.2.5????? org.apache.struts.actions.SwitchAction類
    暫略
    1.1.2?????????? Model
    Struts沒有定義具體的Model層的實(shí)現(xiàn),Model層通常是和業(yè)務(wù)邏輯緊密相關(guān)的,還通常有持續(xù)化的要求,Struts目前沒有考慮到這一層,但是,不管在開源世界還是商業(yè)領(lǐng)域,都有一些都別優(yōu)秀的工具可以為Model層次的開發(fā)提供便利,例如優(yōu)秀的O/R Mapping開源框架Hibernate。
    1.1.3?????????? View
    通常,Web應(yīng)用的UI由以下文件組成:
    ????????? HTML
    ????????? JSP
    而JSP中通常包含以下組件:
    ????????? 自定義標(biāo)簽
    ????????? DTO(Data Transfer Object數(shù)據(jù)傳輸對(duì)象)
    在Struts中,還包含了以下兩種常用的組件:
    ????????? Struts ActionForms
    ????????? 資源綁定(java resource bundles),例如將標(biāo)簽的顯示內(nèi)容,錯(cuò)誤提示的內(nèi)容通過配置文件來(lái)配置,這樣可以為實(shí)現(xiàn)國(guó)際化提供基礎(chǔ)。
    由此可見,Struts對(duì)于傳統(tǒng)的Web UI所作的擴(kuò)充就是Struts ActionForms和資源綁定,接下來(lái)對(duì)其進(jìn)行進(jìn)一步描述。
    1.1.3.1??? 使用 Struts ActionForm
    在Struts框架中,ActionForm負(fù)責(zé)在用戶和業(yè)務(wù)邏輯層之間來(lái)回地傳遞用戶輸入的數(shù)據(jù)。框架會(huì)自動(dòng)收集用戶輸入并以form bean的方式將這些數(shù)據(jù)傳遞給Action,然后,form bean可以被傳遞到業(yè)務(wù)層。不過,為了減少表示層和業(yè)務(wù)層的耦合,不建議將ActionForm 直接傳遞給業(yè)務(wù)層,而建議代之為DTO。即在Action中利用form bean的數(shù)據(jù)創(chuàng)建合適的DTO,然后傳遞給業(yè)務(wù)層。下面的步驟描述了Struts框架在每一次請(qǐng)求中,是如何處理ActionForm的:
    1、???? 檢查是否已經(jīng)配置ActionForm映射到Action;
    2、???? 如果某一個(gè)ActionForm被映射到Action,利用配置文件中action元素的name屬性查找相匹配的ActionForm配置信息;
    3、???? 檢查是否已經(jīng)存在該ActionForm的實(shí)例(instance);
    4、???? 如果存在該ActionForm的實(shí)例,并且符合當(dāng)前請(qǐng)求的需要,則重用這個(gè)實(shí)例;
    5、???? 否則,創(chuàng)建該ActionForm的實(shí)例,并且將其保存在合適的生存區(qū)域中(生存區(qū)域 (scope)的設(shè)置請(qǐng)查看action元素,scope表示該實(shí)例的生存期限,一般來(lái)說(shuō),有request,session,application等幾種);
    6、???? 調(diào)用ActionForm實(shí)例的reset()方法;
    7、???? 遍歷請(qǐng)求參數(shù),根據(jù)不同的參數(shù)名,調(diào)用ActionForm實(shí)例和參數(shù)名相對(duì)應(yīng)的setter方法,設(shè)置參數(shù)值到ActionForm實(shí)例中;
    8、? 最后,如果validate屬性設(shè)置為true,則調(diào)用ActionForm實(shí)例的validate()方法,該方法可以返回任何錯(cuò)誤,主要為校驗(yàn)錯(cuò)誤。
    對(duì)于每一個(gè)需要傳遞form數(shù)據(jù)的HTML頁(yè)面,必須使用一個(gè)ActionForm,同一個(gè)ActionForm可以被多個(gè)不同的頁(yè)面使用,只要HTMLFORM域和ActionForm的屬性相匹配即可。
    下面是一個(gè)ActionForm的示例:
    package com.oreilly.struts.banking.form;
    ?
    import javax.servlet.http.HttpServletRequest;
    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionError;
    import org.apache.struts.action.ActionErrors;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionMapping;
    import org.apache.struts.util.MessageResources;
    /**
    ?* This ActionForm is used by the online banking appliation to validate
    ?* that the user has entered an accessNumber and a pinNumber. If one or
    ?* both of the fields are empty when validate( ) is called by the
    ?* ActionServlet, error messages are created.
    ?*/
    public class LoginForm extends ActionForm {
    ? // The user's private ID number
    ? private String pinNumber;
    ? // The user's access number
    ? private String accessNumber;
    ?
    ? public LoginForm(? ) {
    ??? super(? );
    ??? resetFields(? );
    ? }
    ? /**
    ?? * Called by the framework to validate the user has entered values in the
    ?? * accessNumber and pinNumber fields.
    ?? */
    ? public ActionErrors validate(ActionMapping mapping, HttpServletRequest req ){
    ??? ActionErrors errors = new ActionErrors(? );
    ?
    ??? // Get access to the message resources for this application.
    ??? // There's no easy way to access the resources from an ActionForm.
    ??? MessageResources resources =
    ????? (MessageResources)req.getAttribute( Action.MESSAGES_KEY );
    ?
    ??? // Check and see if the access number is missing.
    ??? if(accessNumber == null || accessNumber.length(? ) == 0) {
    ????? String accessNumberLabel = resources.getMessage( "label.accessnumber" );
    ????? ActionError newError =
    ??????? new ActionError("global.error.login.requiredfield", accessNumberLabel );
    ????? errors.add(ActionErrors.GLOBAL_ERROR, newError);
    ??? }
    ?
    ??? // Check and see if the pin number is missing.
    ??? if(pinNumber == null || pinNumber.length(? ) == 0) {
    ????? String pinNumberLabel = resources.getMessage( "label.pinnumber" );
    ????? ActionError newError =
    ??????? new ActionError("global.error.login.requiredfield", pinNumberLabel );
    ????? errors.add(ActionErrors.GLOBAL_ERROR, newError);
    ??? }
    ??? // Return the ActionErrors, if any.
    ??? return errors;
    ? }
    ?
    ? /**
    ?? * Called by the framework to reset the fields back to their default values.
    ?? */
    ? public void reset(ActionMapping mapping, HttpServletRequest request) {
    ??? // Clear out the accessNumber and pinNumber fields.
    ??? resetFields(? );
    ? }
    ? /**
    ?? * Reset the fields back to their defaults.
    ?? */
    ? protected void resetFields(? ) {
    ??? this.accessNumber = "";
    ??? this.pinNumber = "";
    ? }
    ?
    ? public void setAccessNumber(String nbr) {
    ??? this.accessNumber = nbr;
    ? }
    ?
    ? public String getAccessNumber(? ) {
    ??? return this.accessNumber;
    ? }
    ?
    ? public String getPinNumber(? ) {
    ??? return this.pinNumber;
    ? }
    ? public void setPinNumber(String nbr) {
    ??? this.pinNumber = nbr;
    ? }
    }
    Struts框架提供的ActionForm實(shí)現(xiàn)了一些方法,到現(xiàn)在為止,最重要的兩個(gè)方法是reset()和validator():
    public void reset( ActionMapping mapping, HttpServletRequest request );
    public ActionErrors validate( ActionMapping mapping, HttpServletRequest request );
    ?
    ActionForm對(duì)于這兩個(gè)方法的默認(rèn)實(shí)現(xiàn)是不執(zhí)行任何操作,你可以重載這兩個(gè)方法來(lái)執(zhí)行具體的邏輯。
    當(dāng)寫好了ActionForm類之后,需要通知Struts應(yīng)用程序它的存在,以及action和ActionForm之間的映射關(guān)系。在Struts中,是通過配置文件來(lái)實(shí)現(xiàn)這一目的的。第一步,將應(yīng)用所需要用到的所有ActionForm在配置文件的form-beans這一段加以描述,下面的片斷描述了如何通知Struts應(yīng)用程序這三個(gè)ActionForm的存在。
    <form-beans>
    ?<form-bean
    ?? name="loginForm"
    ?? type="com.oreilly.struts.banking.form.LoginForm"/>?
    ?<form-bean
    ?? name="accountInformationForm"
    ?? type="org.apache.struts.action.DynaActionForm">
    ?? <form-property name="accounts" type="java.util.ArrayList"/>
    ?</form-bean>?
    ?<form-bean
    ?? name="accountDetailForm"
    ?? type="org.apache.struts.action.DynaActionForm">
    ?? <form-property
    ???? name="view"
    ???? type="com.oreilly.struts.banking.view.AccountDetailView"/>
    ?</form-bean>???
    </form-beans>
    每一個(gè)form bean的name屬性必須唯一,type屬性定義了該form bean的類,該類必須實(shí)現(xiàn)Struts ActionForm類。下一步就是建立action和form-bean的聯(lián)系,form-bean可以和一到多個(gè)action建立聯(lián)系,通過在action元素的屬性中引用form-bean的name即可完成映射,下面的片斷顯示了LoginAction和form-bean的映射,我們之前已經(jīng)看過這個(gè)片段:
    <action
    ? path="/login"
    ? type="com.oreilly.struts.banking.action.LoginAction"
    ? scope="request"
    ? name="loginForm"
    ? validate="true"
    ? input="/login.jsp">
    ? <forward name="Success" path="/action/getaccountinformation" redirect="true"/>
    ? <forward name="Failure" path="/login.jsp" redirect="true"/>
    </action>
    ?
    在Struts1.1中,添加了一種新類型的action form ,叫做org.apache.struts.action.DynaActionForm,這種類型的action form可以配置為action的映射,它會(huì)自動(dòng)處理HTML form中的數(shù)據(jù)并將其傳遞到Action。DynaActionForm 如何做到自動(dòng)處理HTML form數(shù)據(jù)的呢?DynaActionForm內(nèi)部使用一個(gè)Map來(lái)存放HTML field數(shù)據(jù)。
    在接下來(lái)的一節(jié)中,我們?cè)敿?xì)了解一下DynaActionForm。
    1.1.1.1??? 使用DynaActionForm
    從上一節(jié)的介紹,我們可以看出,使用ActionForm 和我們自己來(lái)編寫類獲取HTML from值,在進(jìn)行處理相比,有不少優(yōu)勢(shì)。ActionForm所封裝的數(shù)據(jù)和行為時(shí)幾乎每一個(gè)web 應(yīng)用程序都需要的,而且在一個(gè)應(yīng)用中會(huì)多次用到,例如一個(gè)信息實(shí)體的增加和修改,可能從不同的角度,不同的頁(yè)面實(shí)現(xiàn)信息實(shí)體的增、改,通過ActionForm就可以復(fù)用,復(fù)用可以統(tǒng)一規(guī)則,減少開發(fā)時(shí)間和維護(hù)工作量。但是,現(xiàn)在對(duì)ActionForm的使用越來(lái)越少,為什么呢?
    第一,?????????? 也是一個(gè)最大的問題,會(huì)使得項(xiàng)目中存在很多ActionForm類,增加了整個(gè)項(xiàng)目類的數(shù)目和維護(hù)復(fù)雜度,有的開發(fā)人員為了避開這個(gè)問題,使用一個(gè)很大的,包含所有HTML from屬性的ActionForm 來(lái)和所有action映射,這種方式我認(rèn)為問題更多,完全失去了封裝的味道。
    第二,?????????? 當(dāng)需要添加或者刪除一個(gè)HTML from 屬性時(shí),如果ActionForm 需要用到這些屬性,就得修改ActionForm ,并且要重新編譯。
    基于這些原因,在Struts1.1框架中,添加了一種新類型的ActionForm,這種ActionForm可以動(dòng)態(tài)變化從而避免創(chuàng)建具體的ActionForm類。這種ActionForm的基類是org.apache.struts.action.DynaActionForm,當(dāng)然,DynaActionForm是從ActionForm擴(kuò)展而來(lái)的。對(duì)于應(yīng)用來(lái)說(shuō),DynaActionForm和ActionForm在以下三個(gè)方面會(huì)有些不同:
    ????????? ActionForm的屬性定義
    ????????? validate()方法
    ????????? reset()方法
    DynaActionForm的屬性不同于ActionForm,ActionForm的屬性是通過具體的類,具體的setter,getter方法來(lái)進(jìn)行屬性值的設(shè)置,獲取,而DynaActionForm是在Struts的配置文件中定義的。
    對(duì)reset()方法調(diào)用的時(shí)機(jī)和ActionForm并無(wú)不同,只是相對(duì)來(lái)說(shuō),在reset()方法被調(diào)用的時(shí)候,你擁有比較少的控制權(quán)。當(dāng)然,可以通過擴(kuò)展DynaActionForm,重載reset()方法。
    對(duì)于從UI端輸入的數(shù)據(jù)的校驗(yàn),相對(duì)來(lái)說(shuō)有些復(fù)雜,它是通過Struts Validator組件來(lái)實(shí)現(xiàn)的,稍后會(huì)詳細(xì)介紹它。
    1.1.1.1.1????? 配置DynaActionForm
    要使用DynaActionForm,首先得在Struts配置文件中添加form-bean元素。在配置文件中,DynaActionForm和ActionForm的不同之處在于,DynaActionForm需要添加一些form-property元素,form-property用來(lái)指定HTML form中的field名字,Struts框架會(huì)通過這些名字的匹配,自動(dòng)將HTML form 各個(gè)field的值封裝到DynaActionForm實(shí)例中。下面的片斷是關(guān)于DynaActionForm的配置文件示例 :
    <form-beans>
    ? <form-bean
    ??? name="loginForm"
    ??? type="org.apache.struts.action.DynaActionForm">
    ?
    ??? <!-在下面制定Form的屬性 -->
    ??? <form-property
    ????? name="email"
    ????? type="java.lang.String "/>
    ??? <form-property
    ????? name="password"
    ????? type="java.lang.String "/>
    ?
    ??? <!-可以為屬性設(shè)置默認(rèn)值 -->
    ??? <form-property
    ????? initial="false"
    ????? name="rememberMe"
    ????? type="java.lang.Boolean "/>
    ? </form-bean>
    <form-beans>
    當(dāng)你HTML form中添加了一個(gè)屬性,需要在DynaActionForm中添加一個(gè)屬性時(shí),就不需要去修改具體的ActionForm類,只需要在配置文件中添加一個(gè)form-property元素即可,大大提高了可擴(kuò)展能力。
    前面我們已經(jīng)了解到,ActionForm的reset()方法默認(rèn)不進(jìn)行任何操作,在DynaActionForm中,reset()方法默認(rèn)將所有屬性設(shè)置為默認(rèn)值,如果在配置文件中沒有為該屬性設(shè)置默認(rèn)值,將會(huì)按照java編程語(yǔ)言的規(guī)范根據(jù)屬性的類型為其進(jìn)行初始化,例如:數(shù)字(int ,double,float)的將會(huì)初始化為0,Ojbect類型將為初始化為null。
    注意:在配置文件中定義的form-property的type屬性,其值為一個(gè)java類名,因此對(duì)于java語(yǔ)言中的主類型,如ini,long必須定義為java.lang.Int,java.lang.Long,其它主類型依次類推。
    1.1.1.1.1????? 使用DynaActionForm執(zhí)行校驗(yàn)規(guī)則
    ?同ActionForm一樣,DynaActionForm也沒有提供validate()方法的默認(rèn)操作,幸運(yùn)的是,Struts提供了另外一種框架來(lái)幫助大家解決校驗(yàn)的問題,這就是Struts Validator框架。Struts Validator 框架由David Winterfeldt編寫,現(xiàn)在已經(jīng)成為Struts主分發(fā)的一部分,它提供基于規(guī)則的校驗(yàn),對(duì)于常用的校驗(yàn),如必填項(xiàng)目,email,電話等等提供了現(xiàn)成的規(guī)則,只需要通過配置文件進(jìn)行配置即可。關(guān)于Struts Validator框架的詳細(xì)介紹,見后續(xù)章節(jié)。
    1.1.1.2???? Validator 框架
    Struts允許在ActionForm的validator()方法中添加校驗(yàn)代碼,對(duì)用戶輸入的數(shù)據(jù)進(jìn)行規(guī)則校驗(yàn),這能很好地工作,但是,這存在一些限制,舉個(gè)簡(jiǎn)單的例子,一個(gè)必填項(xiàng)現(xiàn)在不是必填項(xiàng)了,要滿足這個(gè)簡(jiǎn)單的需求就需要更改ActionForm類的validator()方法,再進(jìn)行重新編譯,很麻煩。本節(jié)我們來(lái)學(xué)習(xí)Validator框架,看它是如何解決校驗(yàn)問題的。
    1.1.1.2.1????? Validator的安裝、配置
    Validator目前是Jakarta Commons 項(xiàng)目的一部分,它也被包含在Struts主分發(fā)里面,可以直接使用Struts中自帶的Validator庫(kù),也可以去網(wǎng)站上下載
    http://jakarta.apache.org/commons/
    Validator需要一些其它庫(kù)的支持,例如Jakarta ORO,不過沒關(guān)系,Struts分發(fā)包里面都包含了,你只需要按照第2章介紹的將Struts安裝到你的應(yīng)用中,就一切ok。
    1.1.1.2.1.1??????????? 配置校驗(yàn)規(guī)則
    前面提到過,Validator是通過校驗(yàn)規(guī)則來(lái)實(shí)施校驗(yàn),這些校驗(yàn)規(guī)則被配置在配置文件中,這意味著不需要修改源代碼就可以方便地更改校驗(yàn)規(guī)則,是不是感覺很不錯(cuò)?Validator所需要的配置文件有兩個(gè):validation-rules.xml和validation.xml。
    1.1.1.2.1.2??????????? validation-rules.xml
    在validation-rules.xml文件中配置了一些全局性的校驗(yàn)規(guī)則,使得你在應(yīng)用程序中使用校驗(yàn)而不用關(guān)注實(shí)現(xiàn)細(xì)節(jié)。這個(gè)配置文件是Validator框架自帶的,可以用在所有Struts應(yīng)用中。它默認(rèn)配置了許多很常用的規(guī)則,一般來(lái)說(shuō),不用去更改它,除非需要擴(kuò)展或修改這些默認(rèn)的校驗(yàn)規(guī)則。
    建議:即使你需要擴(kuò)展一些規(guī)則,也不要去修改validation-rules.xml,而是通過新的配置文件去定義你所擴(kuò)展的校驗(yàn)規(guī)則。
    validator-rules_1_1.dtd定義了validation-rules.xml文件的結(jié)構(gòu),根元素是form-validation,它包含一到多個(gè)global元素,global元素包含一到多個(gè)validator元素。
    每一個(gè)validator元素定義了一個(gè)唯一的校驗(yàn)規(guī)則。下面是validation-rules.xml文件中的一個(gè)片斷,用來(lái)定義必填項(xiàng)(required)校驗(yàn)規(guī)則:
    <validator
    ? name="required"
    ? classname="org.apache.struts.util.StrutsValidator"
    ? method="validateRequired"
    ? methodParams="java.lang.Object,
    ??? org.apache.commons.validator.ValidatorAction,
    ??? org.apache.commons.validator.Field,
    ??? org.apache.struts.action.ActionErrors,
    ??? javax.servlet.http.HttpServletRequest"
    ? msg="errors.required">
    </validator>
    下表詳細(xì)介紹了validator元素每個(gè)屬性的具體含義:
    序號(hào)?屬性?解釋
    1.??????? ?name?賦予校驗(yàn)規(guī)則一個(gè)唯一的名稱,便于在validation-rules.xml文件和應(yīng)用指定的其它校驗(yàn)文件中引用。
    2.??????? ?classname?指定含有具體校驗(yàn)規(guī)則Java Class名,org.apache.struts.util.StrutsValidator是Validator框架自帶的一個(gè)Java Class,它實(shí)現(xiàn)了一些很常用的校驗(yàn)規(guī)則。
    3.??????? ?method?指定含有具體校驗(yàn)規(guī)則Java Class的具體方法,一個(gè)校驗(yàn)規(guī)則有實(shí)現(xiàn)校驗(yàn)的Java Class的一個(gè)方法來(lái)實(shí)現(xiàn)。
    4.??????? ?methodParams?聲明method屬性所指定的方法的參數(shù),參數(shù)之間用逗號(hào)分隔。
    5.??????? ?msg?msg是用來(lái)指定當(dāng)校驗(yàn)不通過時(shí),Validator框架所給出的提示信息。它的值是應(yīng)用所配置的資源文件中的一個(gè)關(guān)鍵字,當(dāng)校驗(yàn)失敗時(shí),Validator框架利用msg所指定的值到應(yīng)用配置的資源文件中去查找匹配記錄。Validator框架默認(rèn)使用以下提示信息:
    ?? errors.required={0} is required.
    ?? errors.minlength={0} cannot be less than {1} characters.
    ?? errors.maxlength={0} cannot be greater than {1} characters.
    ?? errors.invalid={0} is invalid.??
    ?? errors.byte={0} must be a byte.
    ?? errors.short={0} must be a short.
    ?? errors.integer={0} must be an integer.
    ?? errors.long={0} must be a long.
    ?? errors.float={0} must be a float.
    ?? errors.double={0} must be a double.??
    ?? errors.date={0} is not a date.
    ?? errors.range={0} is not in the range {1} through {2}.
    ?? errors.creditcard={0} is not a valid credit card number.
    ?? errors.email={0} is an invalid email address
    可以將上面的這些信息添加到你的Struts應(yīng)用所配置的資源文件(例如:ApplicationResources.properties)中,也可以修改這些值之后,將其添加到配置文件中,示例如下:
    ?errors.required={0} 是必填項(xiàng)。
    6.??????? ?depends?depends指定在本校驗(yàn)規(guī)則的前置校驗(yàn)規(guī)則,下面的片斷定義了一個(gè)最小長(zhǎng)度的校驗(yàn)規(guī)則,含義是在進(jìn)行最小長(zhǎng)度校驗(yàn)之前,會(huì)先調(diào)用required校驗(yàn)規(guī)則確保數(shù)據(jù)不為空:
    <validator
    ? name="minLength"
    ? classname="org.apache.struts.util.StrutsValidator"
    ? method="validateMinLength"
    ? methodParams="java.lang.Object,
    ??? org.apache.commons.validator.ValidatorAction,
    ??? org.apache.commons.validator.Field,
    ??? org.apache.struts.action.ActionErrors,
    ??? javax.servlet.http.HttpServletRequest"
    ? depends="required"
    ? msg="errors.minlength">
    </validator>
    如果存在多個(gè)前置校驗(yàn)規(guī)則,則可以用以下的方式進(jìn)行聲明,各校驗(yàn)規(guī)則之間用逗號(hào)分隔:
    depends="required,integer"
    如果前置校驗(yàn)規(guī)則失敗,則后續(xù)的校驗(yàn)規(guī)則不會(huì)被執(zhí)行。
    7.??????? ?jsFunctionName?可選屬性。用來(lái)指定JavaScript函數(shù)的名字。
    The final attribute supported by the validator element is the jsFunctionName attribute. This optional attribute allows you to specify the name of the JavaScript function. By default, the Validator action name is used.
    ?
    前面已經(jīng)介紹了,org.apache.struts.util.StrutsValidator是Validator框架自帶的一個(gè)校驗(yàn)規(guī)則類,其實(shí)現(xiàn)了一些常用的校驗(yàn)規(guī)則,其包含的校驗(yàn)方法(method)如下所列:
    ?"?????????? validateByte? 檢查值能夠安全地轉(zhuǎn)換為byte
    "???????? validateCreditCard? 檢查值是一個(gè)有效的信用卡號(hào)碼
    "???????? validateDate 檢查值是一個(gè)有效的日期
    "???????? validateDouble檢查值能夠安全地轉(zhuǎn)換為double
    "???????? validateEmail? 檢查值是一個(gè)有效的Email地址
    "???????? validateFloat? 檢查值能夠安全地轉(zhuǎn)換為double
    "???????? validateInteger? 檢查值能夠安全地轉(zhuǎn)換為int
    "???????? validateLong檢查值能夠安全地轉(zhuǎn)換為long
    "???????? validateMask檢查值符合掩碼規(guī)則,掩碼采用規(guī)則表達(dá)式的方式
    "???????? validateMinLength 檢查值的長(zhǎng)度大于等于指定長(zhǎng)度
    "???????? validateMaxLength檢查值的長(zhǎng)度小于指定長(zhǎng)度
    "???????? validateRange檢查值的有效范圍在指定范圍內(nèi)
    "???????? validateRequired檢查值不為null或長(zhǎng)度>0
    "???????? validateShort 檢查值能夠安全地轉(zhuǎn)換為short
    1.1.1.1.1.1??????????? validation.xml
    Validator框架所需要的第二個(gè)配置文件是validation.xml,這個(gè)配置文件是具體應(yīng)用(項(xiàng)目)所特定的,可以根據(jù)你的應(yīng)用(項(xiàng)目)情況進(jìn)行自定義配置。它描述了具體的ActionForm使用validation-rules.xml文件中的哪個(gè)校驗(yàn)規(guī)則進(jìn)行校驗(yàn)。
    validation_1_1.dtd定義了validation.xml的結(jié)構(gòu),根元素為form-validation,其包含0到多個(gè)global元素和一到多個(gè)formset元素:
    <!ELEMENT form-validation (global*, formset+)>
    global元素包含0到多個(gè)constant子元素:
    <!ELEMENT global (constant*)>
    constant子元素和Java里面常量的含義是一樣的,下面的片斷定義了兩個(gè)常量:
    <global>
    ?<constant>
    ? <constant-name>phone</constant-name>
    ? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
    ?</constant>
    ?<constant>
    ? <constant-name>zip</constant-name>
    ? <constant-value>^\d{5}(-\d{4})?$</constant-value>
    ?</constant>
    </global>
    上面的片斷包含了兩個(gè)常量,phone 和zip,這些常量在所有formset元素中有效,在formset中通過名稱引用這些常量。
    下面的片斷展示了一個(gè)簡(jiǎn)單的validation.xml文件說(shuō)明:
    代碼片斷 3.3.3.3.1.3.1
    <form-validation>
    ?<global>
    ? <constant>
    ?? <constant-name>phone</constant-name>
    ?? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
    ? </constant>????????????????
    ?</global>
    ?<formset>??
    ? <form name="checkoutForm">
    ?? <field
    ???? property="phone"
    ???? depends="required,mask">
    ???? <arg0 key="registrationForm.firstname.displayname"/>
    ???? <var>
    ?????? <var-name>mask</var-name>
    ?????? <var-value>${phone}</var-value>
    ???? </var>????
    ??? </field>???
    ?? </form>???????????
    ? </formset>??
    </form-validation>
    在上面的代碼片斷中,var元素應(yīng)用了在global中定義了phone常量,用來(lái)配合對(duì)phone屬性的校驗(yàn)。
    formset元素可以包含兩個(gè)子元素,constant和form。constant元素和global區(qū)域定義的constant元素格式和用途一樣,只不過作用范圍不同,在formset中定義的constant元素其作用范圍只限于該formset覆蓋區(qū)域。Formset元素中的form元素至少要出現(xiàn)一次。DTD描述如下:
    <!ELEMENT formset (constant*, form+)>
    form元素定義了需要進(jìn)行校驗(yàn)的域,其name屬性對(duì)應(yīng)應(yīng)用中分配給form的標(biāo)識(shí),在Struts框架中,就是在Struts配置文件中form-beans區(qū)域定義的ActionForm的name屬性。
    下面是form元素的DTD定義:
    <!ELEMENT form (field+)>
    field元素指明了JavaBean中需要被校驗(yàn)的屬性。在上面的代碼片斷中,在Struts中,ActionForm就是這個(gè)需要被校驗(yàn)的JavaBean。在代碼片斷 3.3.3.3.1.3.1中,定義了對(duì)Struts配置文件中名稱為checkoutForm的ActionForm所擁有的名稱為phone的屬性的校驗(yàn)說(shuō)明,表示checkoutForm的phone屬性為必填項(xiàng)而且符合${phone}所定義的正則表達(dá)式的掩碼規(guī)則。field元素的屬性在下表中具體描述:
    屬性?描述
    property?JavaBean(在Struts為ActionForm)中需要被校驗(yàn)的屬性的名稱。
    depends?應(yīng)用于property指定屬性的校驗(yàn)規(guī)則列表,多個(gè)校驗(yàn)規(guī)則之間用逗號(hào)分隔。
    page?這個(gè)屬性在應(yīng)用于"向?qū)?模式的form中,用來(lái)確保不會(huì)跳頁(yè)訪問。
    indexedListProperty?不會(huì)用
    表3.3.3.3.1.3.1 field元素的屬性列表
    field元素包含以下幾個(gè)子元素,DTD定義如下:
    <!ELEMENT field (msg?, arg0?, arg1?, arg2?, arg3?, var*)>
    msg子元素允許你為該field指定一個(gè)提示信息,校驗(yàn)規(guī)則將會(huì)使用這個(gè)指定的提示信息替代規(guī)則默認(rèn)的提示信息,msg子元素的值必須是應(yīng)用資源文件的一個(gè)關(guān)鍵字(key)。例如:
    <field property="phone" depends="required,mask">
    ? <msg name="mask" key="phone.invalidformat"/>
    ? <arg0 key="registrationForm.firstname.displayname"/>
    ? <var>
    ??? <var-name>mask</var-name>
    ??? <var-value>${phone}</var-value>
    ? </var>??????????????
    </field>
    msg子元素支持三個(gè)屬性,DTD定義如下:
    <!ATTLIST msg name???? CDATA #IMPLIED
    ???????????????? key????? CDATA #IMPLIED
    ???????????????? resource CDATA #IMPLIED >
    name 屬性指定了msg將使用的校驗(yàn)規(guī)則名稱,屬性值必須是在validation-rules.xml 文件中定義的校驗(yàn)規(guī)則。
    key 屬性指定了一個(gè)資源文件的關(guān)鍵字,當(dāng)校驗(yàn)失敗是,該關(guān)鍵字所代表的信息將會(huì)添加到ActionError中。如果你想設(shè)置一個(gè)明確的文本而不是資源文件的關(guān)鍵字,則可以將resource屬性設(shè)置位false,這種情況下,可以將key屬性設(shè)置為一個(gè)明確的文本,如"電話格式不正確!"。
    ?field元素可以包含至多四個(gè)額外的子元素,它們分別命名為arg0, arg1, arg2和arg3,它們用來(lái)向提示信息中添加額外的信息,arg0定義了第一個(gè)可替換的值,arg1定義了第二個(gè)可替換的值,以此類推。每個(gè)arg元素支持三個(gè)屬性,name, key和resource,其含義和之前我們看到的msg元素的同名屬性含義一致。下面是一段應(yīng)用arg元素的示例:
    <field property="phone" depends="required,mask,minLength">
    ? <arg0 key="registrationForm.firstname.displayname"/>
    ? <arg1 name="minlength" key="${var:minLength}" resource="false"/>
    ? <var>
    ?? <var-name>mask</var-name>
    ?? <var-value>${phone}</var-value>
    ? </var>????
    ? <var>
    ?? <var-name>minLength</var-name>
    ?? <var-value>5</var-value>
    ? </var>????????
    </field>?
    field元素包含的最后一個(gè)子元素是var元素,field元素可以包含0到多個(gè)var元素,var 元素可以設(shè)置該field所用到的校驗(yàn)規(guī)則的參數(shù),var-name參數(shù)名,var-value指定參數(shù)值。例如設(shè)置mask規(guī)則的具體的正則表達(dá)式:
    ?<var-name>mask</var-name>
    ?<var-value>${phone}</var-value>
    設(shè)置minLength規(guī)則的最小長(zhǎng)度:
    <var-name>minLength</var-name>
    <var-value>5</var-value>
    當(dāng)然,這個(gè)參數(shù)可以被arg元素應(yīng)用,應(yīng)用語(yǔ)法為:${var:var-name}。
    1.1.1.1.1.1??????????? 插入Validator
    每一個(gè)Struts應(yīng)用需要知道是否使用了Validator框架,可以通過PlugIn(插件)機(jī)制將Validator框架配置到Struts應(yīng)用中。
    下面的代碼演示了如何將Validator作為一個(gè)插件加入到Struts應(yīng)用中,在Struts應(yīng)用的配置文件Struts-config.xml中加入如下代碼片段:
    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    ? <set-property
    ??? property="pathnames"
    ??? value="/WEB-INF/validator-rules.xml,/WEB-INF/validator.xml"/>
    </plug-in>
    粗體部分pathnames屬性的值用來(lái)指定Validator框架所使用的配置文件,多個(gè)配置文件之間用逗號(hào)分隔。
    當(dāng)應(yīng)用啟動(dòng)的時(shí)候,Struts框架將調(diào)用ValidatorPlugIn的init()方法。Validator框架的配置文件將會(huì)加載到內(nèi)存中供應(yīng)用使用。在init()方法被調(diào)用之前,pathnames所指定的值將會(huì)傳遞給ValidatorPlugIn的實(shí)例,ValidatorPlugIn實(shí)例將會(huì)依據(jù)這個(gè)值去加載配置文件。
    1.1.1.2??? 使用帶校驗(yàn)的ActionForm
    你不能使用標(biāo)準(zhǔn)的Struts ActionForm去和Validator配合使用。你必須使用專門為Validator框架設(shè)計(jì)的ActionForm的子類。現(xiàn)在有兩個(gè)子類可以選擇,取決于你是否打算使用動(dòng)態(tài)ActionForms。下面的圖直觀地顯示了ActionForm以及它的后代:
    ?
    如果你打算使用動(dòng)態(tài)ActionForm,為了和Validator框架配合使用,你可以使用DynaValidatorForm,否則,可以使用ValidatorForm。從圖上看出,DynaValidatorForm有個(gè)子類叫做DynaValidatorActionForm,ValidatorForm有個(gè)子類叫做ValidatorActionForm,這兩個(gè)子類在其父類的名字中間加了個(gè)"Action",這兩個(gè)類有什么作用呢?
    同樣,根據(jù)你是否打算使用動(dòng)態(tài)ActionForm,你可以使用DynaValidatorActionForm或ValidatorActionForm,來(lái)配合使用Validator框架,當(dāng)使用這兩個(gè)類時(shí),它們將action的path屬性傳遞給Validator,Validator使用action的名字去查找使用的校驗(yàn)規(guī)則。而使用DynaValidatorForm和ValidatorForm,則是使用的ActionForm 的name屬性去查找匹配校驗(yàn)規(guī)則。(???)
    1.1.1.1.1????? 示例
    第一步,在Struts-config.xml中配置一個(gè)ActionForm,示例如下:
    <form-bean
    ? name="checkoutForm"
    ? type="org.apache.struts.validator.DynaValidatorForm">
    ? <form-property name="firstName" type="java.lang.String"/>
    ? <form-property name="lastName" type="java.lang.String"/>
    ? <form-property name="address" type="java.lang.String"/>???
    ? <form-property name="city" type="java.lang.String"/>
    ? <form-property name="state" type="java.lang.String"/>
    ? <form-property name="postalCode" type="java.lang.String"/>
    ? <form-property name="country" type="java.lang.String"/>
    ? <form-property name="phone" type="java.lang.String"/>???
    </form-bean>
    第二步,在Struts-config.xml中配置一個(gè)Action,示例如下:
    <action
    ?? input="/checkout.jsp"
    ?? name="checkoutForm"
    ?? path="/checkout"
    ?? scope="request"
    ?? type="com.ort.struts.example.checkOutAction"
    ?? validate="true">
    ?</action>
    ?
    第三布,在validation.xml文件中定義如下:
    <formset>
    ? <constant>
    ??? <constant-name>phone</constant-name>
    ??? <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
    ? </constant>????????????????
    ? <constant>
    ??? <constant-name>zip</constant-name>
    ??? <constant-value>^\d{5}(-\d{4})?$</constant-value>
    ? </constant>??????????????????????
    ? <form name="checkoutForm">
    ??? <field
    ????? property="firstName"
    ????? depends="required,mask">
    ????? <arg0 key="label.firstName"/>??????????
    ????? <var>
    ??????? <var-name>mask</var-name>
    ??????? <var-value>^[a-zA-Z]*$</var-value>
    ????? </var>?????????????????
    ??? </field>
    ??? <field
    ????? property="postalCode"
    ????? depends="required,mask">
    ????? <arg0 key="registrationForm.zip"/>
    ????? <var>
    ??????? <var-name>mask</var-name>
    ??????? <var-value>${zip}</var-value>
    ????? </var>
    ??? </field>
    ??? <field
    ????? property="phone"
    ????? depends="required,mask">
    ????? <arg0 key="registrationForm.phone"/>
    ????? <var>
    ??????? <var-name>mask</var-name>
    ??????? <var-value>${phone}</var-value>
    ????? </var>
    ??? </field>????????
    ? </form>???????????
    ?</formset>??
    </form-validation>
    第四部,編寫HTML頁(yè)面如下:
    暫略

    ?


    1.???? JSP 自定義標(biāo)簽庫(kù)
    1.1? 概述
    在JSP開發(fā)中會(huì)遇到一些重復(fù)的工作。而使用自定義標(biāo)簽庫(kù)是一種方法,可以用來(lái)將這些功能封裝起來(lái)并在多個(gè)項(xiàng)目中重新用到它。此外,應(yīng)用邏輯還可以包含在基于服務(wù)器的資源中,比如JavaBeans。這種架構(gòu)顯示出使用自定義標(biāo)簽庫(kù)可以更快更容易地開發(fā)基于Web的應(yīng)用程序。
    有關(guān)JavaBeans和自定義標(biāo)簽庫(kù)的最初想法是:在程序員研究那些包含商務(wù)邏輯(business logic)的類的同時(shí),Web設(shè)計(jì)師可以同步進(jìn)行頁(yè)面設(shè)計(jì)。然后,Web設(shè)計(jì)師可以通過使用簡(jiǎn)單的"連線"將JSP頁(yè)面和這些類聯(lián)系起來(lái)。盡管使用JavaBean會(huì)減少在JSP頁(yè)面中寫代碼的數(shù)量,但你還是得寫程序去使用這些Beans。
    然而使用自定義標(biāo)簽庫(kù)則是一種完全無(wú)需在JSP中寫代碼的好辦法。這并不是說(shuō)自定義標(biāo)簽庫(kù)會(huì)取代JavaBeans,它們都是用來(lái)分離實(shí)際內(nèi)容和顯示形式的。JavaBeans在用于商務(wù)邏輯被重用的設(shè)計(jì)中作用更為明顯。JavaBeans通常能在不同項(xiàng)目的各種頁(yè)面中被用到。另一方面,自定義標(biāo)簽庫(kù)則是一個(gè)特殊頁(yè)面的自定義形式,即便如此,將它重新利用到其他程序中也是很常見的。得到自定義標(biāo)簽庫(kù)的一種方法是自己建一個(gè)。但為什么不使用現(xiàn)成的呢?比如Jakarta Taglibs項(xiàng)目(源自Apache Software Foundation)就提供了一些自定義標(biāo)簽庫(kù),它們可以在不同的JSP應(yīng)用程序中重復(fù)使用。
    1.2? Struts包含的標(biāo)簽庫(kù)
    Struts框架提供了一系列的框架組件,同時(shí),他也提供了一系列的標(biāo)簽(Tag)用于和框架進(jìn)行交互。Struts提供的標(biāo)簽包含在以下四個(gè)標(biāo)簽庫(kù)(Tag libraries)中:
    "???????? HTML
    "???????? Bean
    "???????? Logic
    "???????? Template
    這四個(gè)標(biāo)簽庫(kù)所包含的標(biāo)簽功能各自截然不同,從標(biāo)簽庫(kù)的名字我們可以看出其功能,如,HTML標(biāo)簽庫(kù)是用來(lái)包裝HTML控件的。
    1.3? 在Struts應(yīng)用中使用標(biāo)簽庫(kù)
    和使用其它標(biāo)簽庫(kù)一樣,使用Struts提供的標(biāo)簽庫(kù)只需要簡(jiǎn)單的兩步:
    1、? 在web.xml中聲明標(biāo)簽庫(kù):
    <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>
    ?
    2、? 在JSP頁(yè)面中引入標(biāo)簽庫(kù):
    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
    ?
    1.4? Struts HTML標(biāo)簽庫(kù)
    HTML標(biāo)簽庫(kù)中的標(biāo)簽列表
    標(biāo)簽名?描述
    base?包裝HTML的base元素
    button?包裝HTML的 button類型的input元素
    cancel?包裝HTML cancel 按鈕
    checkbox?包裝HTML checkbox 類型的輸入域
    errors?有條件地顯示一些error消息,顯示ActionErrors信息
    file?包裝HTML文件上傳輸入域
    form?定義HTML form 元素
    frame?包裝HTML frame 元素
    hidden?包裝HTML hidden 輸入域
    html?包裝 HTML中的 html 元素
    image?包裝 "image"類型的輸入域
    img?包裝HTML的 img 元素
    javascript?包裝根據(jù)ValidatorPlugIn提供的校驗(yàn)規(guī)則所提供的javascript校驗(yàn)?zāi)_本
    link?包裝超鏈接
    messages?有條件地顯示一些提示信息,顯示ActionMessages信息
    multibox?包裝多選輸入框
    option?包裝一個(gè)選擇輸入框
    options?包裝一批選擇輸入框
    optionsCollection?包裝一批選擇輸入框集
    password?包裝密文輸入框
    radio?包裝單選輸入框
    reset?包裝"重置"功能的按鈕
    rewrite?包裝一個(gè)URL
    select?包裝一個(gè)選擇輸入框
    submit?包裝一個(gè)提交按鈕
    text?包裝一個(gè)文本輸入框
    textarea?包裝一個(gè)備注輸入框
    在這里,不打算對(duì)每一個(gè)標(biāo)簽的使用進(jìn)行詳細(xì)說(shuō)明,要想了解每一個(gè)標(biāo)簽的使用,請(qǐng)查看Struts官方文檔。
    接下來(lái),我們著重學(xué)習(xí)一下幾個(gè)非常重要的標(biāo)簽的使用,舉一反三,通過這幾個(gè)標(biāo)簽的使用,我想,即使不去看官方文檔,也能夠?qū)ζ渌鼧?biāo)簽的使用有個(gè)基本的了解。
    1.1.1?????????? form標(biāo)簽
    Struts的form標(biāo)簽是最重要的標(biāo)簽之一,他包裝了HTML的標(biāo)準(zhǔn)form標(biāo)簽,提供了將HTML form和ActionForm 連接起來(lái)的功能。
    HTML form中的每一個(gè)域?qū)?yīng)ActionForm的一個(gè)屬性,當(dāng)提交HTML from后,Struts根據(jù)匹配關(guān)系,將HTML from域的值賦給ActionForm的同名屬性。下表列舉了form標(biāo)簽的屬性,并且針對(duì)每一個(gè)屬性加以詳細(xì)說(shuō)明:
    Struts form標(biāo)簽屬性列表
    Name?Description
    action?form提交的目標(biāo)地址,action用來(lái)選擇一個(gè)Struts Action對(duì)提交后的客戶請(qǐng)求進(jìn)行處理。通過和action的path屬性進(jìn)行匹配來(lái)選擇Struts Action。
    如果在web.xml中設(shè)置的servlet映射是擴(kuò)展名映射,則可以用以下方式進(jìn)行Action匹配(擴(kuò)展名可以不作為匹配內(nèi)容):
    <html:form action="login.do" focus="accessNumber">
    上面的語(yǔ)句表示form提交后,交給path屬性值為login的Action進(jìn)行處理
    如果在web.xml中設(shè)置的servlet映射是路徑映射,則action的值必須完全匹配Struts Action的path屬性值,例如:
    <html:form action="login" focus="accessNumber">
    enctype?提交form使用的編碼方式
    focus?頁(yè)面初始化時(shí)光標(biāo)定位的輸入控件名
    method?提交請(qǐng)求的HTTP方式('POST' or 'GET')
    name?對(duì)應(yīng)的ActionForm的名字 ,如果不指定,則使用Struts Action在配置文件中name屬性所指定的ActionForm。
    onreset?當(dāng)form重置時(shí)執(zhí)行的Javascript
    onsubmit?當(dāng)form提交時(shí)執(zhí)行的javascript
    scope?與form對(duì)應(yīng)的ActionForm的有效區(qū)域,可以為request 或session
    style?CSS樣式表The CSS styles to be applied to this HTML element.
    styleClass?CSS樣式類別
    styleId?HTML元素的ID
    target?form提交的目標(biāo)frame
    type?form對(duì)應(yīng)的ActionForm的類名
    ?
    1.2? Struts Logic標(biāo)簽庫(kù)
    Struts Logic標(biāo)簽庫(kù)中包含的標(biāo)簽列表
    Tag name?Description
    empty?如果標(biāo)簽parameter,propertie等屬性所指定的變量值為null或空字符串,則處理標(biāo)簽包含的內(nèi)容
    equal?如果標(biāo)簽parameter,propertie等屬性所指定的變量的值等于標(biāo)簽value屬性所指定的值,則處理標(biāo)簽所包含的內(nèi)容,如:
    <logic:equal value="modify" property="action"? name="projectForm">
    ??? <bean:message key="project.project_modify"/>
    </logic:equal>
    上面的示例表示,如果projectForm的action屬性等于modify,則處理<bean:message key="project.project_modify"/>語(yǔ)句。
    forward?Forward control to the page specified by the ActionForward entry.
    greaterEqual?Evaluate the nested body content of this tag if the requested variable is greater than or equal to the specified value.
    greaterThan?Evaluate the nested body content of this tag if the requested variable is greater than the specified value.
    iterate?Repeat the nested body content of this tag over a specified collection.
    lessEqual?Evaluate the nested body content of this tag if the requested variable is less than or equal to the specified value.
    lessThan?Evaluate the nested body content of this tag if the requested variable is less than the specified value.
    match?Evaluate the nested body content of this tag if the specified value is an appropriate substring of the requested variable.
    messagesNotPresent ?Generate the nested body content of this tag if the specified message is not present in this request.
    messagesPresent?Generate the nested body content of this tag if the specified message is present in this request.
    notEmpty?Evaluate the nested body content of this tag if the requested variable is neither null nor an empty string.
    notEqual?Evaluate the nested body content of this tag if the requested variable is not equal to the specified value.
    notMatch?Evaluate the nested body content of this tag if the specified value is not an appropriate substring of the requested variable.
    notPresent?Generate the nested body content of this tag if the specified value is not present in this request.
    present?Generate the nested body content of this tag if the specified value is present in this request.
    redirect?Render an HTTP redirect.
    ?
    執(zhí)行比較功能的標(biāo)簽通用屬性表
    Name?Description
    name?The name of a bean to use to compare against the value attribute. If the property attribute is used, the value is compared against the property of the bean, instead of the bean itself.
    parameter?The name of a request parameter to compare the value attribute against.
    property?The variable to be compared is the property (of the bean specified by the name attribute) specified by this attribute. The property reference can be simple, nested, and/or indexed.
    scope?The scope within which to search for the bean named by the name attribute. All scopes will be searched if not specified.
    value?The constant value to which the variable, specified by another attribute(s) of this tag, will be compared.
    ?
    示例:
    To check whether a particular request parameter is present, you can use the Logic present tag:
    <logic:present parameter="id">
    ? <!-- Print out the request parameter id value -->
    </logic:present>
    To check whether a collection is empty before iterating over it, you can use the notEmpty tag:
    <logic:notEmpty name="userSummary" property="addresses">
    ? <!-- Iterate and print out the user's addresses -->
    </logic:notEmpty>
    Finally, here's how to compare a number value against a property within an ActionForm:
    <logic:lessThan property="age" value="21">?
    ? <!-- Display a message about the user's age -->
    </logic:lessThan>
    1.1? Struts Bean標(biāo)簽庫(kù)
    Struts Bean標(biāo)簽庫(kù)中的標(biāo)簽列表
    Tag name?Description
    cookie?Define a scripting variable based on the value(s) of the specified request cookie.
    define?Define a scripting variable based on the value(s) of the specified bean property.
    header?Define a scripting variable based on the value(s) of the specified request header.
    include?Load the response from a dynamic application request and make it available as a bean.
    message?Render an internationalized message string to the response.
    page?Expose a specified item from the page context as a bean.
    parameter?Define a scripting variable based on the value(s) of the specified request parameter.
    resource?Load a web application resource and make it available as a bean.
    size?Define a bean containing the number of elements in a Collection or Map.
    struts?Expose a named Struts internal configuration object as a bean.
    write?Render the value of the specified bean property.
    ?
    1.2? Struts Template標(biāo)簽庫(kù)
    Struts Template標(biāo)簽庫(kù)中的標(biāo)簽列表
    Tag name?Description
    insert?Retrieve (or include) the specified template file, and then insert the specified content into the template's layout. By changing the layout defined in the template file, any other file that inserts the template will automatically use the new layout.
    put?Create a request-scope bean that specifies the content to be used by the get tag. Content can be printed directly or included from a JSP or HTML file.
    get?Retrieve content from a request-scope bean, for use in the template layout.
    ?

    1.???? 示例
    示例實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的項(xiàng)目信息CRUD(添加、查詢、更新、刪除)+分頁(yè)顯示的功能。
    其中項(xiàng)目有如下屬性:
    ????????? 項(xiàng)目編號(hào)
    ????????? 項(xiàng)目名稱
    ????????? 項(xiàng)目所屬區(qū)域(區(qū)域存在與一個(gè)字典表中)
    ????????? 項(xiàng)目分成比例(0.00%--100.00%)
    ?
    數(shù)據(jù)庫(kù)結(jié)構(gòu)定義如下所示(采用Oracle8i數(shù)據(jù)庫(kù),如果采用其它數(shù)據(jù)庫(kù)請(qǐng)作相應(yīng)變化):
    SITES --區(qū)域表
    Name?Datatype?Null Option?Comment?Is PK
    SITECODE?VARCHAR2(10)?NOT NULL?區(qū)域號(hào)?Yes
    SITENAME?VARCHAR2(30)?NOT NULL?區(qū)域名稱?No
    ?
    PROJECTS --項(xiàng)目表
    Name?Datatype?Null Option?Comment?Is PK
    PROJECTCODE?VARCHAR2(10)?NOT NULL?項(xiàng)目編號(hào)?Yes
    PROJECTNAME?VARCHAR2(30)?NOT NULL?項(xiàng)目名稱?No
    SITECODE?VARCHAR2(10)?NOT NULL?所屬區(qū)域號(hào)?
    DISCOUNT?NUMBER?NULL?項(xiàng)目分成比例?
    ?
    附:在ORACLE8i中創(chuàng)建數(shù)據(jù)表的語(yǔ)句為:
    --創(chuàng)建區(qū)域表
    CREATE TABLE SITES (
    ?SITECODE VARCHAR2(10) NOT NULL,
    ?SITENAME VARCHAR2(30) NOT NULL
    );
    --添加主鍵
    ALTER TABLE SITES
    ?????? ADD? ( CONSTRAINT SITES_PKSITECODE PRIMARY KEY (SITECODE)
    ?????? USING INDEX );?
    --創(chuàng)建項(xiàng)目信息表
    CREATE TABLE PROJECTS(
    ?PROJECTCODE VARCHAR2(10) NOT NULL,
    ?PROJECTNAME VARCHAR2(30) NOT NULL,
    ?SITECODE VARCHAR2(10) NOT NULL,
    ?DISCOUNT NUMBER NULL
    );
    --添加主鍵索引
    ALTER TABLE PROJECTS
    ?????? ADD? ( CONSTRAINT PROJECTS_PKPROJECTCODE PRIMARY KEY (PROJECTCODE)
    ?????? USING INDEX );?
    --添加外鍵索引
    CREATE INDEX FK_PROJECTSSITECODE ON PROJECTS
    (
    ?????? SITECODE? ASC
    );
    --添加外鍵約束
    ?ALTER TABLE PROJECTS
    ?????? ADD? ( CONSTRAINT FK_PROJECTSSITECODE
    ????????????? FOREIGN KEY (SITECODE)
    ???????????????????????????? REFERENCES SITES ) ;
    --創(chuàng)建區(qū)域表
    CREATE TABLE SITES (
    ?SITECODE VARCHAR2(10) NOT NULL,
    ?SITENAME VARCHAR2(30) NOT NULL
    );
    --添加主鍵
    ALTER TABLE SITES
    ?????? ADD? ( CONSTRAINT SITES_PKSITECODE PRIMARY KEY (SITECODE)
    ?????? USING INDEX );?
    --創(chuàng)建項(xiàng)目信息表
    CREATE TABLE PROJECTS(
    ?PROJECTCODE VARCHAR2(10) NOT NULL,
    ?PROJECTNAME VARCHAR2(30) NOT NULL,
    ?SITECODE VARCHAR2(10) NOT NULL,
    ?DISCOUNT NUMBER NULL
    );
    --添加主鍵索引
    ALTER TABLE PROJECTS
    ?????? ADD? ( CONSTRAINT PROJECTS_PKPROJECTCODE PRIMARY KEY (PROJECTCODE)
    ?????? USING INDEX );?
    --添加外鍵索引
    CREATE INDEX FK_PROJECTSSITECODE ON PROJECTS
    (
    ?????? SITECODE? ASC
    );
    --添加外鍵約束
    ?ALTER TABLE PROJECTS
    ?????? ADD? ( CONSTRAINT FK_PROJECTSSITECODE
    ????????????? FOREIGN KEY (SITECODE)
    ???????????????????????????? REFERENCES SITES ) ;
    ?
    1.1? 命名規(guī)范
    1、? 所有jsp,action映射路徑均為小寫字母,如有需要可以使用小寫字母+數(shù)字:例如:
    /projectlist,/projetlist.jsp
    2、? 所有<html:form>中的元素(如文本框,列表框等)名稱都使用java規(guī)范的變量命名方式(變量由一個(gè)或多個(gè)單詞組成,第一個(gè)單詞小寫,第一個(gè)單詞后的單詞首字母大寫),例如:
    <html:text styleClass="input" maxlength="10" property="projectCode" size="30"/>
    3、? 其它方面均遵守SUN推薦的編碼規(guī)范。
    1.2? 文件
    1.2.1?????????? projectlist.jsp
    該jsp頁(yè)面用來(lái)顯示項(xiàng)目信息列表,并提供查詢功能。同時(shí),提供按鈕將用戶導(dǎo)向到添加、修改、刪除功能。
    1.2.2?????????? projectform.jsp
    在執(zhí)行添加、修改操作之前,需要提供一個(gè)form供用戶輸入數(shù)據(jù),在執(zhí)行刪除操作之前,需要提供一個(gè)form將被刪除數(shù)據(jù)顯示出來(lái),供用戶確認(rèn)。該jsp頁(yè)面就是用來(lái)滿足這些需要,提供對(duì)單條項(xiàng)目信息的顯示,根據(jù)具體的操作類型(創(chuàng)建、修改、刪除),數(shù)據(jù)被顯示在可編輯的輸入控件中或不可編輯的label(文本標(biāo)簽)上。
    1.2.3?????????? success.jsp
    添加、修改、刪除等操作正常完成后,提供一個(gè)頁(yè)面顯示"恭喜"信息,使得用戶能夠清楚知道自己的行為已經(jīng)生效 。
    1.2.4?????????? failed.jsp
    添加、修改、刪除等操作異常失敗,提供一個(gè)頁(yè)面顯示"失敗"信息,使得用戶能夠清楚知道自己的行為已經(jīng)失敗 。
    1.2.5?????????? ProjectListSearchAction.java
    "Project"+"List"+"Search"+"Action",組成了這個(gè)Action的名字,這是我個(gè)人的命名風(fēng)格,表示這個(gè)Action會(huì)處理項(xiàng)目列表和查詢事務(wù)。在projectlist.jsp被裝載之前,ProjectListSearchAction需要將數(shù)據(jù)加載到scope指定的地方,供projectlist.jsp顯示,當(dāng)用戶從projectlist.jsp中提交查詢請(qǐng)求,該Action需要處理查詢,并加載數(shù)據(jù),供projectlist.jsp顯示。
    1.2.6?????????? ProjectFormLoadAction
    這個(gè)Action用來(lái)處理在顯示projectform.jsp之前,將所需要的數(shù)據(jù)加載到scope指定的范圍中,供projectform使用。
    ?
    1.2.7?????????? ProjectFormSaveAction.java
    這個(gè)Action用來(lái)處理用戶在projectform.jsp中提交的數(shù)據(jù),根據(jù)用戶的操作類型,完成具體的操作,并將合適的提示頁(yè)面(success.jsp or failed.jsp)顯示給用戶。
    1.2.8?????????? web.xml
    在Struts安裝那一節(jié),我們已經(jīng)知道web.xml文件的作用,通過這個(gè)文件,我們可以將ActionServlet配置好,用以截獲用戶對(duì)Struts應(yīng)用的訪問請(qǐng)求。下面是示例程序所用的web.xml內(nèi)容:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "<web-app>
    ? <display-name>simpledemo</display-name>
    ? <description>Demo for using STRUTS to do some thing about CRUD(Create,Read,Update,Delete) and any more....</description>
    ? <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>2</param-value>
    ??? </init-param>
    ??? <load-on-startup>2</load-on-startup>
    ? </servlet>
    ? <servlet>
    ??? <servlet-name>debugjsp</servlet-name>
    ??? <description>Added to compile JSPs with debug info</description>
    ??? <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    ??? <init-param>
    ????? <param-name>classdebuginfo</param-name>
    ????? <param-value>true</param-value>
    ??? </init-param>
    ??? <load-on-startup>3</load-on-startup>
    ? </servlet>
    ? <servlet-mapping>
    ??? <servlet-name>action</servlet-name>
    ??? <url-pattern>*.do</url-pattern>
    ? </servlet-mapping>
    ? <servlet-mapping>
    ??? <servlet-name>debugjsp</servlet-name>
    ??? <url-pattern>*.jsp</url-pattern>
    ? </servlet-mapping>
    ? <session-config>
    ??? <session-timeout>20</session-timeout>
    ? </session-config>
    ? <welcome-file-list>
    ??? <welcome-file>projectsearch.do</welcome-file>
    ? </welcome-file-list>
    ? <taglib>
    ??? <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
    ??? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    ? </taglib>
    ? <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>
    ? <taglib>
    ??? <taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
    ??? <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
    ? </taglib>
    ? <taglib>
    ??? <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
    ??? <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
    ? </taglib>
    ? <taglib>
    ??? <taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
    ??? <taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
    ? </taglib>
    </web-app>
    1.1.1?????????? 資源文件
    使用資源文件來(lái)放置標(biāo)簽顯示值,提示信息等,如果你的Struts應(yīng)用有國(guó)際化的要求,那么資源文件一定要好好地利用,就算沒有國(guó)際化需求,使用資源文件也可以統(tǒng)一應(yīng)用程序用語(yǔ)(例如統(tǒng)一的提示信息,標(biāo)簽信息等),而且當(dāng)用語(yǔ)發(fā)生變化(如"小時(shí)"變成"鐘頭" ),很容易統(tǒng)一進(jìn)行修改。下面是示例程序所用到的資源文件內(nèi)容(application.properties):
    #System global labels
    button_cancel = 取消
    button_edit = 修改
    button_delete = 刪除
    button_save = 保存
    button_submit = 確認(rèn)
    ?
    #lables for project
    projectcontroller.title = 管理項(xiàng)目
    project.project_create = 添加項(xiàng)目
    project.project_modify = 修改項(xiàng)目
    project.project_list = 已添加項(xiàng)目列表
    project.projectcode = 項(xiàng)目代碼
    project.projectname = 項(xiàng)目名稱
    project.discount = 項(xiàng)目分成比例
    project.site = 所屬小區(qū)
    # Standard error messages for validator framework checks
    errors.required={0} is required.
    errors.minlength={0} cannot be less than {1} characters.
    errors.maxlength={0} cannot be greater than {2} characters.
    errors.invalid={0} is invalid.
    errors.byte={0} must be an byte.
    errors.short={0} must be an short.
    errors.integer={0} must be an integer.
    errors.long={0} must be an long.
    errors.float={0} must be an float.
    errors.double={0} must be an double.
    errors.date={0} is not a date.
    errors.range={0} is not in the range {1} through {2}.
    errors.creditcard={0} is not a valid credit card number.
    errors.email={0} is an invalid e-mail address.
    那么,如何在Struts應(yīng)用中引用資源文件呢?
    首先需要在Struts配置文件(Struts-config.xml)中指明配置文件的路徑,在配置文件中添加如下一行信息:
    <message-resources parameter="ApplicationResources_CN" />
    parameter所指的就是配置文件,注意,為什么這里指明的是applicationResources_CN,而不是上面提到的application.properties?這是為了能在Struts中正確顯示中文,利用jdk自帶的native2ascii程序?qū)pplication.properties作了編碼轉(zhuǎn)換,編碼轉(zhuǎn)換后的文件名為ApplicationResources_CN.properties,擴(kuò)展名可以省略。
    需要注意的是,改配置文件一定要放在classpath范圍內(nèi),一般放置在WEB-INF/classes目錄下,如果放在classes的子目錄下,其指引方式和java包一樣,例如在WEB-INF/classes/com目錄下,則應(yīng)該用如下語(yǔ)句指引:
    <message-resources parameter="com.ApplicationResources_CN" />
    小技巧
    進(jìn)行中文編碼轉(zhuǎn)換的命令如下:
    native2ascii -encoding gb2312 application.properties ApplicationResources_CN.properties
    在配置文件聲明了對(duì)資源文件的引用之后,就可以在Struts提供的標(biāo)簽以及校驗(yàn)框架等其它地方使用這些資源,具體使用方法請(qǐng)查看相關(guān)標(biāo)簽和配置文件說(shuō)明。
    1.1.2?????????? struts-config.xml
    該文件定義了Struts應(yīng)用中的Action,ActionForm,插件,資源引用等信息,示例程序struts-config.xml文件內(nèi)容如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "
    <struts-config>
    ? <form-beans>
    ????? <!--項(xiàng)目增、刪、改form定義 begin-->
    ??? <form-bean name="projectForm" type="com.ort.strutsdemo.simpledemo.ui.ProjectForm">
    ??? </form-bean>
    ??? <!--項(xiàng)目增、刪、改form定義 結(jié)束-->
    ??? <!--進(jìn)入項(xiàng)目增、刪、改界面之前參數(shù)傳遞Form begin-->
    ??? <form-bean name="projectLoadForm" type="org.apache.struts.action.DynaActionForm">
    ????? <form-property name="action" size="10" type="java.lang.String" initial="create"/>
    ????? <form-property name="projectCode" size="30" type="java.lang.String" initial=""/>
    ??? </form-bean>
    ??? <!--進(jìn)入項(xiàng)目增、刪、改界面之前參數(shù)傳遞Form end-->
    ??? <!--項(xiàng)目查詢Form定義 Begin-->
    ??? <form-bean name="projectSearchForm" type="org.apache.struts.action.DynaActionForm">
    ??????? <form-property name="projectCode" size="10" type="java.lang.String" initial=""/>
    ??????? <form-property name="projectCodeSign" size="10" type="java.lang.String" initial=""/>
    ??????? <form-property name="projectName" size="10" type="java.lang.String" initial=""/>
    ??????? <form-property name="projectNameSign" size="10" type="java.lang.String" initial=""/>
    ??????? <form-property name="page" size="10" type="java.lang.Integer" initial="1"/>
    ??????? <form-property name="pageCount" size="10" type="java.lang.Integer" initial="1"/>
    ??? </form-bean>
    ??? <!--項(xiàng)目查詢Form定義 End-->
    ? </form-beans>
    ? <global-forwards>
    ??? <forward name="projectlist" path="/projectlist.jsp" />
    ??? <forward name="projectform" path="/projectform.jsp" />
    ??? <forward name="failed" path="/failed.jsp" />
    ??? <forward name="success" path="/success.jsp" />
    ? </global-forwards>
    ?
    ? <action-mappings>
    ??? <action input="projectform" name="projectForm" path="/projectformsave" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectFormSaveAction" validate="true">
    ????? <forward name="success" path="/projectsearch.do" redirect="true" />
    ????? <forward name="success.return" path="/projectsearch.do" redirect="true" />
    ????? <forward name="cancel" path="/projectsearch.do" redirect="true" />
    ????? <forward name="failed" path="/failed.jsp" />
    ????? <forward name="failed.return" path="/projectsearch.do" />
    ??? </action>
    ??? <action input="projectlist" name="projectLoadForm" path="/projectformload" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectFormLoadAction" validate="false">
    ????? <forward name="success" path="/projectform.jsp" />
    ????? <forward name="failed" path="/failed.jsp" />
    ????? <forward name="failed.return" path="/projectsearch.do" redirect="true" />
    ??? </action>
    ???? <action input="projectlist" name="projectSearchForm" path="/projectsearch" scope="request" type="com.ort.strutsdemo.simpledemo.controller.ProjectListSearchAction" validate="false">
    ????? <forward name="success" path="/projectlist.jsp"/>
    ????? <forward name="failed" path="/failed.jsp" />
    ????? <forward name="failed.return" path="/projectsearch.do" redirect="true" />
    ??? </action>
    ? </action-mappings>
    ? <controller>
    ??? <set-property property="inputForward" value="true" />
    ? </controller>
    ? <message-resources parameter="ApplicationResources_CN" />
    ? <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    ??? <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
    ? </plug-in>
    </struts-config>
    1.1? CRUD
    CRUD(Create,Retrieve or Read?,Update,Delete)是信息管理系統(tǒng)中經(jīng)常要執(zhí)行的操作的簡(jiǎn)稱。接下來(lái)我們分別描述這幾種操作的執(zhí)行流程。
    1.1.1?????????? 查詢項(xiàng)目信息
    執(zhí)行項(xiàng)目信息查詢列表顯示的文件請(qǐng)求處理順序如下:
    1、? ProjectListSearchAction
    2、? projectlist.jsp
    ?
    ProjectListSearchAction代碼如下:
    package com.ort.strutsdemo.simpledemo.controller;
    ?
    /**
    ?* <p>Title: Struts Training </p>
    ?* <p>Description: Struts內(nèi)部培訓(xùn)Demo</p>
    ?* <p>Copyright: Copyright (c) 2004</p>
    ?* <p>Company: </p>
    ?* @author Liuz
    ?* @version 1.0
    ?*/
    import org.apache.struts.action.*;
    import javax.servlet.http.*;
    import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
    import com.boss.module.operation.object.Project;
    import com.ort.strutsdemo.simpledemo.Constants;
    import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
    import com.boss.module.operation.object.searchresult.help.ResultSetIterator;
    import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
    import com.boss.module.operation.object.searchgene.ProjectSearchGene;
    ?
    /**
    ?*
    ?* <p>Title: Struts Training </p>
    ?* <p>Description: 項(xiàng)目查詢結(jié)果數(shù)據(jù)的裝載</p>
    ?* <p>Copyright: Copyright (c) 2004</p>
    ?* <p>Company: ORT</p>
    ?* @author Liuz
    ?* @version 1.0
    ?*/
    public class ProjectListSearchAction
    ??? extends Action {
    ??? BusinessDelegate businessDelegate = null;
    ??? public ActionForward execute(ActionMapping actionMapping,
    ???????????????????????????????? ActionForm actionForm,
    ???????????????????????????????? HttpServletRequest request,
    ???????????????????????????????? HttpServletResponse response) {
    ??????? try {
    ??????????? DynaActionForm form = (DynaActionForm) actionForm;
    ??????????? //定義分頁(yè)所需要變量
    ??????????? int page= ((Integer)form.get("page")).intValue();
    ??????????? int pageSize = 5;
    ??????????? int allSize = 0;
    ??????????? int pageCount = 0;
    ??????????? //獲取用戶輸入查詢值,并形成查詢條件
    ??????????? String projectCode = (String)form.get("projectCode");
    ??????????? String projectCodeSign = (String)form.get("projectCodeSign");
    ??????????? String projectName = (String)form.get("projectName");
    ??????????? String projectNameSign = (String)form.get("projectNameSign");
    ??????????? ProjectSearchGene searchGene = new ProjectSearchGene(); //searchGene為一查詢精靈,用以處理查詢操作,在此不用過多關(guān)注,有機(jī)會(huì)在另文介紹
    ??????????? searchGene.setProjectCode(projectCode, projectCodeSign);
    ??????????? searchGene.setProjectName(projectName, projectNameSign);
    ?
    ?
    ??????????? //裝載當(dāng)前頁(yè)面所需要顯示項(xiàng)目列表數(shù)據(jù)
    ???????????? BusinessDelegate businessDelegate = BusinessDelegate.getInstance(); //業(yè)務(wù)層操作,不用關(guān)注
    ???????????? ResultSetIterator projectIterator = businessDelegate.
    ???????????????? getProjectIterator(searchGene,pageSize);
    ???????????? java.util.List projects = projectIterator.getElements(page);
    ???????????? if (projects == null) {
    ???????????????? projects = new java.util.ArrayList();
    ???????????? }
    ???????????? //將項(xiàng)目列表查詢結(jié)果放置到請(qǐng)求對(duì)象中
    ??????????? request.removeAttribute(Constants.PROJECT_LISTFORM_KEY);
    ??????????? request.setAttribute(Constants.PROJECT_LISTFORM_KEY, projects);
    ??????????? //計(jì)算總頁(yè)數(shù)
    ??????????? allSize = projectIterator.getAllSize();
    ??????????? pageCount = (allSize % pageSize == 0) ? allSize / pageSize :
    ??????????????? allSize / pageSize + 1;
    ?????????? // System.err.print(pageCount);
    ??????????? form.set("pageCount", new Integer(pageCount));
    ?
    ??????????? //裝載當(dāng)前頁(yè)面所需要現(xiàn)實(shí)小區(qū)信息
    ??????????? ResultSetIterator siteIterator = businessDelegate.getSitesIterator();
    ??????????? java.util.List sites = siteIterator.getElements(1);
    ??????????? request.getSession().setAttribute(Constants.SITE_LISTFORM_KEY,
    ????????????????????????????????????????????? sites);
    ??????????? //重定向到mapping中配置的input頁(yè)面
    //??????????? return actionMapping.findForward("success");
    ??????????? return actionMapping.getInputForward();
    ??????? }
    ??????? catch (Exception ex) {
    ??????????? com.ipbs.util.Log.println(
    ??????????????? "[ProjectListSearchAction.java][Exception]:" + ex.getMessage());
    ??????????? ExceptionBean exception = new ExceptionBean();
    ??????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
    ??????????? exception.setReturnPath(actionMapping.findForward("failed.return").
    ??????????????????????????????????? getPath());
    ??????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
    ??????????? return actionMapping.findForward("failed");
    ??????? }
    ??? }
    }
    在上面的代碼中,應(yīng)用了一個(gè)常量類(Constants),為了讓大家看得更加明白,將其代碼顯示如下:
    package com.ort.strutsdemo.simpledemo;
    ?
    /**
    ?* <p>Title: Struts Training </p>
    ?* <p>Description: Struts內(nèi)部培訓(xùn)Demo</p>
    ?* <p>Copyright: Copyright (c) 2004</p>
    ?* <p>Company: </p>
    ?* @author Liuz
    ?* @version 1.0
    ?*/
    ?
    public class Constants {
    ? public static final String PROJECT_SINGLEFORM_KEY = "projectForm";
    ? public static final String PROJECT_LISTFORM_KEY = "PROJECTS";
    ? public static final String PROJECT_SEARCHFORM_KEY = "SEARCHPROJECTS";
    ? public static final String PROJECT_CONTROLLERFORM_KEY = "PROJECTCONTROLLER";
    ? public static final String SITE_LISTFORM_KEY = "SITES";
    ? public static final String SITE_SINGLEFORM_KEY = "SITE";
    ?
    ? public static final String EXCEPTION_BEAN = "EXCEPTIONBEAN";
    ? /**
    ?? * 通過識(shí)別異常的基礎(chǔ)類型,返回易懂的提示信息
    ?? * @param ex Exception 異常
    ?? * @return String 異常的描述信息
    ?? */
    ? public static final String getExceptionMsg(Exception ex){
    ????? String returnMsg="";
    ????? if(ex!=null){
    ????????? if (ex instanceof com.boss.module.operation.command.exception.AlreadyExistException){
    ????????????? returnMsg = "數(shù)據(jù)已存在!";
    ????????????? return returnMsg;
    ????????? }else if (ex instanceof com.boss.module.operation.command.exception.DbException){
    ????????????? returnMsg = "數(shù)據(jù)庫(kù)錯(cuò)誤!";
    ????????????? return returnMsg;
    ????????? }else if(ex instanceof com.boss.module.operation.command.exception.InvalidObjectException){
    ????????????? returnMsg = "無(wú)效的數(shù)據(jù)!";
    ????????????? return returnMsg;
    ????????? }else if(ex instanceof com.boss.module.operation.command.exception.InvalidPkException){
    ????????????? returnMsg = "無(wú)效的主鍵!";
    ????????????? return returnMsg;
    ????????? }else if(ex instanceof com.boss.module.operation.command.exception.NotFoundException){
    ????????????? returnMsg = "數(shù)據(jù)不存在!";
    ????????????? return returnMsg;
    ????????? }else if(ex instanceof com.boss.module.operation.command.exception.UnAuthorizationException){
    ????????????? returnMsg = "無(wú)權(quán)限!";
    ????????????? return returnMsg;
    ????????? }else if(ex instanceof com.boss.module.operation.object.searchresult.exception.IteratorException){
    ????????????? returnMsg = "獲取列表數(shù)據(jù)異常!";
    ????????????? return returnMsg;
    ????????? }
    ????? }
    ??? return returnMsg;
    ? }
    }
    ?
    projectlist.jsp內(nèi)容如下:
    <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
    <%@ taglib uri="/WEB-INF/struts-nested.tld" prefix="nested" %>
    <%@ taglib uri="/WEB-INF/struts-template.tld" prefix="template" %>
    <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
    <%@ page contentType="text/html; charset=GB2312" %>
    ?
    <html:html>
    <head>
    <!-projectlist.title為資源引用-->
    <title><bean:message key="projectlist.title"/></title>
    <link href="css/main.css" rel="stylesheet" type="text/css">
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312"></head>
    <body bgcolor="#ffffff">
    <html:form action="projectsearch">
    ??? <table width="70%"? border="0" align="center" cellpadding="3" cellspacing="1" class="tablebodycolor">
    ??? <tr class="tdbodycolor">
    ????? <td colspan="2" class="tdheadcolor">
    ????????????? <bean:message key="project.project_search"/>
    ????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td width="15%"><div align="right"><bean:message key="project.projectcode"/></div></td>
    ????? <td width="85%">
    ????????? <html:select property="projectCodeSign">
    ????????????? <html:option value="like">like</html:option>
    ????????????? <html:option value="not like">not like</html:option>
    ????????????? <html:option value="=">equal</html:option>
    ????????????? <html:option value="<>">not equal</html:option>
    ????????? </html:select>
    ?<html:text styleClass="input" maxlength="30" property="projectCode" size="30"/>
    ????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td><div align="right"><bean:message key="project.projectname"/></div></td>
    ????? <td>
    ????????? <html:select property="projectNameSign">
    ????????????? <html:option value="like">like</html:option>
    ????????????? <html:option value="not like">not like</html:option>
    ????????????? <html:option value="=">equal</html:option>
    ????????????? <html:option value="<>">not equal</html:option>
    ????????? </html:select>
    ????????? <html:text styleClass="input" maxlength="30" property="projectName" size="30"/>
    ????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ?
    ?? <tr class="tdbodycolor">
    ????? <td colspan="2"><div align="center">
    ????????? <html:hidden property="page"/>
    ????????? <html:hidden property="pageCount"/>
    ??????? <html:submit><bean:message key="button_search"/></html:submit>
    ????? </div></td>
    ??? </tr>
    ??? </tr>
    ? </table>
    </html:form>
    <table width="70%" border="0" align="center" cellpadding="2" cellspacing="1" class="tablebodycolor">
    ? <tr class="tdheadcolor">
    ??? <td height="19" colspan="6" class="tdbodycolor">
    ?????? <input type="button" name="button_create"
    ??????????? value="<bean:message key="button_create"/>" onclick="javascript:window.location='projectformload.do?action=create'"/>
    ??? </td>
    ? </tr>
    </table>
    <table width="70%" border="0" align="center" cellpadding="2" cellspacing="1" class="tablebodycolor">
    ? <tr class="tdheadcolor">
    ??? <td height="19" colspan="6" class="tdbodycolor"><bean:message key="project.project_list"/></td>
    ? </tr>
    ? <tr class="tdheadcolor">
    ??? <td width="92"><div align="center"><bean:message key="project.projectcode"/></div></td>
    ??? <td width="182"><div align="center"><bean:message key="project.projectname"/></div></td>
    ??? <td width="70"><div align="center"><bean:message key="project.discount"/></div></td>
    ??? <td width="139"><div align="center"><bean:message key="project.site"/></div></td>
    ??? <td width="83"><div align="center"><bean:message key="button_edit" /></div></td>
    ??? <td width="83"><div align="center"><bean:message key="button_delete"/></div></td>
    ? </tr>
    ? <logic:iterate id="project" name="PROJECTS">
    ? <tr class="tdbodycolor">
    ??? <td><div align="center"><bean:write name="project" property="projectCode"/></div></td>
    ??? <td><div align="center"><bean:write name="project" property="projectName"/></div></td>
    ??? <td><div align="center"><bean:write name="project" property="discount"/></div></td>
    ??? <td><div align="center"><bean:define id="site" name="project" property="site">
    ?????????????????????????? </bean:define>
    ?????????????????????????? <bean:write name="site" property="siteName"/></div></td>
    ??? <td><div align="center">
    ??????? <input type="button" name="button_edit"
    ??????????? value="<bean:message key="button_edit"/>" onclick="javascript:window.location='projectformload.do?action=modify&projectCode=<bean:write name="project" property="projectCode"/>'"/>
    ??? </div></td>
    ??? <td><div align="center">
    ?????? <input type="button" name="button_delete"
    ??????????? value="<bean:message key="button_delete"/>" onclick="javascript:window.location='projectformload.do?action=delete&projectCode=<bean:write name="project" property="projectCode"/>'"/>
    ??? </div></td>
    ? </tr>
    ? </logic:iterate>
    </table>
    ?
    <!------分頁(yè)----->
    <script language="javascript">
    ?function firstPage(){
    ??????????? document.projectSearchForm.page.value = 1;
    ??????????? document.projectSearchForm.submit();
    ?}
    ??????? function previousPage(){
    ??????????? document.projectSearchForm.page.value = parseInt(document.projectSearchForm.page.value)-1;
    ??????????? document.projectSearchForm.submit();
    ??????? }
    ??????? function nextPage(){
    ??????????? document.projectSearchForm.page.value = parseInt(document.projectSearchForm.page.value)+1;
    ??????????? document.projectSearchForm.submit();
    ??????? }
    ??????? function lastPage(){
    ??????????? document.projectSearchForm.page.value = document.projectSearchForm.pageCount.value;
    ??????????? document.projectSearchForm.submit();
    ??????? }
    </script>
    <table width=600 cellspacing="0" cellpadding="0" align="center">
    ??????? <tr>
    ????????? <td colspan=2><br>
    ????????????? <div align="center">
    ??<bean:define id="currPageOb" property="page" name="projectSearchForm"/>
    ??????????????? <bean:define id="pageCountOb" property="pageCount" name="projectSearchForm"/>
    ??????????????? <%int currPage = ((Integer)(currPageOb)).intValue();
    ??????????????? int pageCount = ((Integer)(pageCountOb)).intValue();%>
    ??????????????? <%if(currPage>1){%>
    ?????? ?????????? <a href="javascript:firstPage();">首頁(yè)</a>
    ????????? ?? <a href="javascript:previousPage();">上一頁(yè)</a>
    ??????????????? <%}%>
    ??????????????? <%if(pageCount>currPage){%>
    ????????? ?? <a href="javascript:nextPage();">下一頁(yè)</a>
    ???????????????????? ?<a href="javascript:lastPage();">末頁(yè)</a>
    ??????????????? <%}%>
    ??????????????? 當(dāng)前頁(yè):<%=currPage%>,
    ??????????????? 共<%=pageCount%>頁(yè)
    ????????????? </div>
    ???????????? </td>
    ??????? </tr>
    </table>
    <!------分頁(yè)----->
    ?
    </body>
    </html:html>
    界面顯示如下:
    ?
    1.1.1?????????? 創(chuàng)建項(xiàng)目信息
    執(zhí)行項(xiàng)目信息查詢列表顯示的文件請(qǐng)求處理順序如下:
    1、? ProjectListSearchAction
    2、? projectlist.jsp
    3、? ProjectFormLoadAction
    4、? projectform.jsp
    5、? ProjectFormSaveAction
    6、? success.jsp or failed.jsp
    7、? ProjectListSearchAction
    ?
    ProjectFormLoadAction內(nèi)容如下:
    package com.ort.strutsdemo.simpledemo.controller;
    ?
    ?
    /**
    ?* <p>Title: Struts Training </p>
    ?* <p>Description: Struts內(nèi)部培訓(xùn)Demo</p>
    ?* <p>Copyright: Copyright (c) 2004</p>
    ?* <p>Company: </p>
    ?* @author Liuz
    ?* @version 1.0
    ?*/
    import org.apache.struts.action.*;
    import javax.servlet.http.*;
    import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
    import com.boss.module.operation.object.Project;
    import com.ort.strutsdemo.simpledemo.Constants;
    import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
    import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
    import com.boss.module.operation.object.searchresult.help.ResultSetIterator;
    ?
    public class ProjectFormLoadAction
    ??? extends Action {
    ? BusinessDelegate businessDelegate = null;
    ? public ActionForward execute(ActionMapping actionMapping,
    ?????????????????????????????? ActionForm actionForm,
    ?????????????????????????????? HttpServletRequest request,
    ?????????????????????????????? HttpServletResponse response) {
    ??? businessDelegate = BusinessDelegate.getInstance();
    ??? DynaActionForm form = (DynaActionForm)actionForm;
    ??? String action = (String)form.get("action");
    ??? String projectCode = (String)form.get("projectCode");
    ??? try {
    ??????? Project project = null;
    ??????? if ( ("create".equals(action))) {
    ??????????? project = new Project();
    ??????? }
    ??????? else {
    ??????????? project = businessDelegate.getProject(projectCode);
    ??????? }
    ??????? project.setAction(action);
    ??????? ProjectForm modifyForm = new ProjectForm();
    ??????? org.apache.commons.beanutils.PropertyUtils.copyProperties(
    ??????????? modifyForm, project);
    ??????? if (! ("create".equals(action))) {
    ??????????? modifyForm.setSiteCode(project.getSite().getSiteCode());
    ??????? }
    ??????? request.setAttribute(Constants.PROJECT_SINGLEFORM_KEY, modifyForm); //為下一個(gè)頁(yè)面,即projectform.jsp的顯示提供數(shù)據(jù)
    ??????? //裝載當(dāng)前頁(yè)面所需要顯示小區(qū)信息
    ??????? ResultSetIterator siteIterator = businessDelegate.getSitesIterator();
    ??????? java.util.List sites = siteIterator.getElements(1);
    ??????? request.getSession().setAttribute(Constants.SITE_LISTFORM_KEY,
    ????????????????????????????????????????? sites);
    ?
    ??????? return actionMapping.findForward("success");
    ??? }
    ??? catch (Exception ex) {
    ??????? com.ipbs.util.Log.println("[ProjectFormLoadAction.java][Exception]:"+ex.getMessage());
    ??????? ExceptionBean exception = new ExceptionBean();
    ??????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
    ??????? exception.setReturnPath(actionMapping.findForward("failed.return").getPath());
    ??????? request.setAttribute(Constants.EXCEPTION_BEAN,exception);
    ??????? return actionMapping.findForward("failed");
    ??? }
    ? }
    }
    ?
    projectform.jsp內(nèi)容如下:
    <%
    /**
    ?* 文件名:projectform.jsp
    ?* 描述:在執(zhí)行添加、修改操作之前,需要提供一個(gè)form供用戶輸入數(shù)據(jù),在執(zhí)行刪除操作之前,
    ?*???? 需要提供一個(gè)form將被刪除數(shù)據(jù)顯示出來(lái),供用戶確認(rèn)。該jsp頁(yè)面就是用來(lái)滿足這些需要,
    ?*???? 提供對(duì)單條項(xiàng)目信息的顯示,根據(jù)具體的操作類型(創(chuàng)建、修改、刪除),數(shù)據(jù)被顯示在可編
    ?*???? 輯的輸入控件中或不可編輯的
    ?*
    ?*+------------------------------------------------------------------------------
    ?* 更改歷史
    ?* 更改時(shí)間?????????????????? 更改人??????? 目標(biāo)版本???????????? 更改內(nèi)容
    ?*+------------------------------------------------------------------------------
    ?* 2004-04-21 16:09????????? liuz???????? 1.00.000????????????? 創(chuàng)建
    ?*
    ?*
    ?*/
    %>
    <%@ page contentType="text/html; charset=GB2312" %>
    <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
    <html>
    <head>
    <title>
    <logic:equal value="modify" property="action"? name="projectForm">
    ??? <bean:message key="project.project_modify"/>
    </logic:equal>
    <logic:equal value="create" property="action" name="projectForm">
    ? <bean:message key="project.project_create"/>
    </logic:equal>
    <logic:equal value="delete" property="action" name="projectForm">
    ? <bean:message key="project.project_delete"/>
    </logic:equal>
    </title>
    <link href="css/main.css" rel="stylesheet" type="text/css">
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    </head>
    <body bgcolor="#ffffff">
    ?
    <html:form action="/projectformsave" method="post" onsubmit="return validateProjectForm(this);">
    ? <table width="70%"? border="0" align="center" cellpadding="3" cellspacing="1" class="tablebodycolor">
    ??? <tr class="tdbodycolor">
    ????? <td colspan="2" class="tdheadcolor">
    ????????? <logic:equal value="modify" property="action" name="projectForm">
    ????? ??<bean:message key="project.project_modify"/>
    ????????? </logic:equal>
    ????????? <logic:equal value="create" property="action" name="projectForm">
    ????????????? <bean:message key="project.project_create"/>
    ????????? </logic:equal>
    ????????? <logic:equal value="delete" property="action" name="projectForm">
    ????????????? <bean:message key="project.project_delete"/>
    ????????? </logic:equal>
    ????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td width="15%"><div align="right"><bean:message key="project.projectcode"/></div></td>
    ????? <td width="85%">
    ????????? <logic:equal value="modify" property="action" name="projectForm">
    ????? ??<html:hidden property="projectCode" write="true"/>
    ????????? </logic:equal>
    ????????? <logic:notEqual value="modify" property="action" name="projectForm">
    ????????????? <html:text styleClass="input" maxlength="10" property="projectCode" size="30"/>
    ????????? </logic:notEqual>
    ????????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td><div align="right"><bean:message key="project.projectname"/></div></td>
    ????? <td><html:text styleClass="input" maxlength="30" property="projectName" size="30"/></td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td><div align="right"><bean:message key="project.discount"/></div></td>
    ????? <td><html:text styleClass="input" maxlength="10" property="discount" size="10"/>
    ????? %</td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td><div align="right"><bean:message key="project.site"/></div></td>
    ????? <td>
    ????????? <html:select property="siteCode">
    ????????????? <html:options? collection="SITES"? labelProperty="siteName"? property="siteCode"/>
    ????????? </html:select>
    ????? </td>
    ??? </tr>
    ??? <tr class="tdbodycolor">
    ????? <td colspan="2"><div align="center">
    ????????? <html:hidden property="action" />
    ??????? <html:submit><bean:message key="button_submit"/></html:submit>
    ???????????? <html:cancel><bean:message key="button_cancel"/>
    ???????????? </html:cancel>
    ????? </div></td>
    ??? </tr>
    ? </table>
    </html:form>
    <html:javascript formName="projectForm"
    ??????? dynamicJavascript="true"
    ???????? staticJavascript="false"/>
    <script language="Javascript1.1" src="staticJavascript.jsp"></script>
    </body>
    </html>
    界面顯示效果如下:
    ?圖6.3.2.1 修改項(xiàng)目圖
    ?圖6.3.2.2 添加項(xiàng)目圖
    ?圖6.3.2.3 刪除確認(rèn)圖
    最后,信息新增、修改或刪除確認(rèn)后,需要提交給ProjectFormSaveAction進(jìn)行,ProjectFormSaveAction內(nèi)容如下:
    package com.ort.strutsdemo.simpledemo.controller;
    ?
    import org.apache.struts.action.*;
    import javax.servlet.http.*;
    import com.ort.strutsdemo.simpledemo.business.BusinessDelegate;
    import com.boss.module.operation.object.Project;
    import com.boss.module.operation.object.Site;
    import com.ort.strutsdemo.simpledemo.Constants;
    import com.ort.strutsdemo.simpledemo.ui.ProjectForm;
    import com.ort.strutsdemo.simpledemo.controller.exception.ExceptionBean;
    import com.ipbs.util.web.ParamUtils;
    /**
    ?*
    ?* <p>Title: Struts Training </p>
    ?* <p>Description: 項(xiàng)目管理功能頁(yè)面的導(dǎo)向,以及頁(yè)面所需要數(shù)據(jù)的初始化,同時(shí),處理刪除操作</p>
    ?* <p>Copyright: Copyright (c) 2004</p>
    ?* <p>Company: </p>
    ?* @author Liuz
    ?* @version 1.0
    ?*/
    public class ProjectFormSaveAction
    ??? extends Action {
    ? BusinessDelegate businessDelegate = null;
    ? public ActionForward execute(ActionMapping actionMapping,
    ?????????????????????????????? ActionForm actionForm,
    ?????????????????????????????? HttpServletRequest request,
    ?????????????????????????????? HttpServletResponse response) {
    ??? businessDelegate = BusinessDelegate.getInstance();
    ??? ProjectForm form = (ProjectForm)actionForm;
    ??? ActionForward forward = null;
    ??? String action = form.getAction();
    ??? if (this.isCancelled(request)) {
    ??????? return (actionMapping.findForward("cancel"));
    ??? }
    ??? if("modify".equals(action)){
    ??????? forward = modifyProject(actionMapping, actionForm,
    ?????????????????????????????????????????? request, response);
    ??? }
    ??? else if ("create".equals(action)) {
    ????? forward = createProject(actionMapping, actionForm,request, response);
    ??? }else if("delete".equals(action)){
    ?????? forward = deleteProject(actionMapping, actionForm,request, response);
    ??? }
    ??? return forward;
    ? }
    ?
    ?
    ? public ActionForward modifyProject(ActionMapping actionMapping,
    ???????????????????????????????????? ActionForm actionForm,
    ??????????????????????????????????? HttpServletRequest request,
    ??????????????????????????????????? HttpServletResponse response
    ??????????????????????????????????? )
    {
    ???? ProjectForm form = (ProjectForm) actionForm;
    ???? String projectCode = form.getProjectCode();
    ???? String projectName = form.getProjectName();
    ???? String siteCode = form.getSiteCode();
    ???? double discount = form.getDiscount();
    ????? try{
    ????????? Site site = businessDelegate.getSite(siteCode);
    ????????? Project project = new Project();
    ????????? project.setProjectCode(projectCode);
    ????????? project.setProjectName(projectName);
    ????????? project.setDiscount(discount);
    ????????? project.setSite(site);
    ????????? businessDelegate.modifyProject(project);
    ????????? return actionMapping.findForward("success");
    ????? }catch(Exception ex){
    ????????? com.ipbs.util.Log.println(
    ????????????? "[ProjectFormSaveAction.java][Exception]:" + ex.getMessage());
    ????????? ExceptionBean exception = new ExceptionBean();
    ????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
    ????????? exception.setReturnPath(actionMapping.getPath());
    ????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
    ????????? return actionMapping.findForward("failed");
    ????? }
    ? }
    ?
    ? public ActionForward createProject(ActionMapping actionMapping,
    ???????????????????????????????????? ActionForm actionForm,
    ??????????????????????????????????? HttpServletRequest request,
    ??????????????????????????????????? HttpServletResponse response
    ??????????????????????????????????? ) {
    ???? ProjectForm form = (ProjectForm) actionForm;
    ???? String projectCode = form.getProjectCode();
    ???? String projectName = form.getProjectName();
    ???? String siteCode = form.getSiteCode();
    ???? double discount = form.getDiscount();
    ???? try{
    ???????? Site site = businessDelegate.getSite(siteCode);
    ???????? Project project = new Project();
    ???????? project.setProjectCode(projectCode);
    ???????? project.setProjectName(projectName);
    ???????? project.setDiscount(discount);
    ???????? project.setSite(site);
    ???????? businessDelegate.createProject(project);
    ???????? return actionMapping.findForward("success");
    ???? }catch(Exception ex){
    ???????? com.ipbs.util.Log.println(
    ????????????? "[ProjectFormSaveAction.java][Exception]:" + ex.getMessage());
    ????????? ExceptionBean exception = new ExceptionBean();
    ????????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
    ????????? exception.setReturnPath(actionMapping.getPath());
    ????????? request.setAttribute(Constants.EXCEPTION_BEAN, exception);
    ????????? return actionMapping.findForward("failed");
    ???? }
    ? }
    ?
    ? public ActionForward deleteProject(ActionMapping actionMapping,
    ???????????????????????????????????? ActionForm actionForm,
    ??????????????????????????????????? HttpServletRequest request,
    ??????????????????????????????????? HttpServletResponse response
    ??????????????????????????????????? ) {
    ???? ProjectForm form = (ProjectForm)actionForm;
    ???? String projectCode = form.getProjectCode();
    ???? try{
    ???????? businessDelegate.deleteProject(projectCode);
    ???????? request.removeAttribute(Constants.PROJECT_SINGLEFORM_KEY);
    ???????? return actionMapping.findForward("success");
    ???? }catch(Exception ex){
    ???????? com.ipbs.util.Log.println(
    ???????????? "[ProjectControllerAction.java][Exception]:" + ex.getMessage());
    ???????? ExceptionBean exception = new ExceptionBean();
    ???????? exception.setErrorMsg(Constants.getExceptionMsg(ex));
    ???????? exception.setReturnPath(actionMapping.findForward("failed.return").getPath());
    ???????? request.setAttribute(Constants.EXCEPTION_BEAN,exception);
    ???????? return actionMapping.findForward("failed");
    ?
    ???? }
    ? }
    ?
    }
    至此為止,該示例項(xiàng)目所使用的絕大部分內(nèi)容已經(jīng)介紹完了,因?yàn)闀r(shí)間問題,沒有對(duì)示例程序進(jìn)行很詳細(xì)的一一講解,代碼上有些簡(jiǎn)單的注釋,希望你能夠看明白,如果大家對(duì)以上代碼有疑問會(huì)發(fā)現(xiàn)什么問題,可以發(fā)郵件和我溝通(
    lzasp800@sina.com),另外,我保證上面的代碼是真實(shí)可運(yùn)行的,如果用心看,也是可以看明白的(當(dāng)然只有文章中所列的代碼是無(wú)法通過編譯的)。
    個(gè)人覺得,做Struts項(xiàng)目非常重要的一個(gè)環(huán)節(jié)就是請(qǐng)求處理流程設(shè)計(jì),這也是本示例強(qiáng)調(diào)的重點(diǎn),弄清楚了這一點(diǎn),看代碼也會(huì)容易很多。
    1.1.1?????????? 修改項(xiàng)目信息
    請(qǐng)求處理流程如下,具體文件內(nèi)容請(qǐng)查看前述內(nèi)容:
    1、? ProjectListSearchAction
    2、? projectlist.jsp
    3、? ProjectFormLoadAction
    4、? projectform.jsp
    5、? ProjectFormSaveAction
    6、? success.jsp or failed.jsp
    7、? ProjectListSearchAction
    ?
    1.1.2?????????? 刪除項(xiàng)目信息
    請(qǐng)求處理流程如下,具體文件內(nèi)容請(qǐng)查看前述內(nèi)容:
    1、? ProjectListSearchAction
    2、? projectlist.jsp
    3、? ProjectFormLoadAction
    4、? projectform.jsp
    5、? ProjectFormSaveAction
    6、? success.jsp or failed.jsp
    7、? ProjectListSearchAction
    ?

    ?

    posted on 2006-06-28 14:22 程序-人生 閱讀(4803) 評(píng)論(0)  編輯  收藏

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
    博客園   IT新聞   Chat2DB   C++博客   博問  
     
    主站蜘蛛池模板: 亚洲丁香婷婷综合久久| 在线视频亚洲一区| 羞羞视频免费网站含羞草| a级日本高清免费看| 欧美男同gv免费网站观看| 国产精品亚洲综合一区| 亚洲AV一二三区成人影片| 国产日韩久久免费影院| 成年女人午夜毛片免费看| 亚洲人成网77777亚洲色| 亚洲综合一区无码精品| 国内少妇偷人精品视频免费| 男人的好看免费观看在线视频| 亚洲无码在线播放| 亚洲精品动漫免费二区| 污污网站18禁在线永久免费观看| 日本特黄特色aa大片免费| 亚洲色图在线播放| 九九免费观看全部免费视频| 欧美好看的免费电影在线观看 | 久久亚洲精品成人777大小说| 亚洲综合激情五月丁香六月| a在线视频免费观看| 伊人久久亚洲综合影院| 亚洲13又紧又嫩又水多| 国偷自产一区二区免费视频| 免费欧洲美女牲交视频| 2019亚洲午夜无码天堂| 无码AV片在线观看免费| 色噜噜AV亚洲色一区二区| 国产亚洲一卡2卡3卡4卡新区| 中文免费观看视频网站| 国产亚洲人成网站观看| 青青草国产免费国产是公开| 成人a视频片在线观看免费| 亚洲黄色三级视频| 久久久久久免费一区二区三区| 亚洲国产一级在线观看| 亚洲AV无码资源在线观看| 青青青国产在线观看免费| 亚洲另类激情综合偷自拍|