Struts視圖組件

Struts框架的視圖負(fù)責(zé)為客戶提供動態(tài)網(wǎng)頁內(nèi)容。Struts視圖主要由JSP網(wǎng)頁構(gòu)成,此外,Struts框架還提供了Struts客戶化標(biāo)簽和ActionForm Bean,這些組件提供對國際化、接收用戶輸入的表單數(shù)據(jù)、表單驗證和錯誤處理等的支持,使開發(fā)者可以把更多的經(jīng)歷放在實現(xiàn)業(yè)務(wù)需求上。

 

1 視圖概述

視圖使模型的外在表現(xiàn)形式,用戶通過視圖來了解模型的狀態(tài)。同一個模型可以有多種視圖。

Struts框架中,視圖主要由JSP組件構(gòu)成,此外,視圖還可以包含以下組件:

· HTML文檔

· JSP客戶化標(biāo)簽

· JavaScriptstylesheet

· 多媒體文件

· 消息資源(Resource Bundle

· ActionForm Bean

 

2 在視圖中使用JavaBean

JavaBean使可重用的、平臺獨立的Java組件,JavaBean支持屬性、事件、方法和持久化。Struts框架僅利用了JavaBean的一小部分特性。在Struts應(yīng)用中的JavaBean和普通的Java類很相似,不過,它應(yīng)該遵守以下規(guī)范:

· 必需提供不帶參數(shù)的構(gòu)造方法。

· Bean的所有屬性提供公共類型的get/set方法。

· 對于boolean類型的屬性,如果存在isXXX()方法,那么該方法返回boolean類型的屬性值。

· 對于數(shù)組類型的屬性,應(yīng)該提供getXXX(int index)setXXX(int index, PropertyElement value)方法,用來讀取或設(shè)置數(shù)組中的元素。

 

2,1 DTO數(shù)據(jù)傳輸對象

在【Struts模型組件】介紹過可以利用JavaBean來創(chuàng)建業(yè)務(wù)對象,實體業(yè)務(wù)對象包含了模型的狀態(tài)信息。此外,Struts框架還利用JavaBean來創(chuàng)建數(shù)據(jù)傳輸對象(Data Transfer Object,簡稱DTO)。DTO用于在不同的層之間傳遞數(shù)據(jù)。

不將模型層的業(yè)務(wù)對象直接傳遞到視圖層(從技術(shù)角度來說使可以實現(xiàn)的),而是采用DTO來傳輸數(shù)據(jù),這樣做有兩個好處:

· 減少傳輸數(shù)據(jù)的冗余,提高傳輸效率。

· 有助于實現(xiàn)各個層之間的獨立,使每個層分工明確。模型層負(fù)責(zé)業(yè)務(wù)邏輯,視圖層負(fù)責(zé)向用戶戰(zhàn)士模型狀態(tài)。采用DTO,模型層對視圖層屏蔽了業(yè)務(wù)邏輯細(xì)節(jié),向視圖層提供可以直接現(xiàn)實給用戶的數(shù)據(jù)。

 

2.2 Struts框架提供的DTOActionForm Bean

    ActionForm BeanStruts框架提供的DTO,用于在視圖層和控制層之間傳遞HTML表單數(shù)據(jù)。控制層可以從ActionForm Bean中讀取用戶輸入的表單數(shù)據(jù),也可以把來自模型層的數(shù)據(jù)存放到ActionForm Bean中,然后把它返回給視圖。ActionForm Bean還具有表單驗證功能,可以為模型層過濾不合法的數(shù)據(jù)。

    在【Struts模型組件】,曾經(jīng)強調(diào)過模型層應(yīng)該和Web應(yīng)用層保持獨立。由于ActionForm類中使用了Servlet API,因此不提倡直接把ActionForm Bean傳給模型層,而應(yīng)該在控制層把ActionForm Bean的數(shù)據(jù)重新組裝到自定義的DTO中,再把它傳遞給模型層。

 

3 使用ActionForm

 

3.1 使用ActionForm

ActionForm Bean有兩種存在范圍:requestsession。如果ActionForm存在于request范圍,它僅在當(dāng)前的請求/響應(yīng)生命周期中有效。在請求從一個Web組件轉(zhuǎn)發(fā)到另一個Web組件的過程中,ActionForm實例一直有效。當(dāng)服務(wù)器把響應(yīng)結(jié)果返回給客戶,ActionForm實例及其包含的數(shù)據(jù)就會被銷毀。如果ActionForm存在于session范圍,同一個ActionForm實例在整個HTTP會話中有效。

當(dāng)控制器接受到請求時,如果請求訪問的Web組件為Action,并且為這個Action配置了和ActionForm的映射,控制器將從requestsession范圍中取出ActionForm實例,如果該實例不存在,就會自動創(chuàng)建一個新的實例。當(dāng)控制器接受到一個新的請求時,ActionForm的生命周期如下:

· 控制器接收到請求

· requestsession范圍中取出ActionForm實例,如果該實例不存在,就自動創(chuàng)建一個新的實例。

· 調(diào)用ActionFormreset()方法

· ActionForm實例保存在requestsession范圍中

· 把用戶輸入的表單數(shù)據(jù)組裝到ActionForm

· 如果<action>validate屬性為true,則調(diào)用ActionFormvalidate()方法。

· (1) 如果存在驗證錯誤,把請求轉(zhuǎn)發(fā)給<action>input屬性指定的Web組件,ActionForm實例依然保持在requestsession范圍內(nèi)。

 

       (2) 如果無驗證錯誤,調(diào)用Actionexecute()方法,把ActionForm實例傳遞給execute()方法。

           · 把請求轉(zhuǎn)發(fā)給其他Web組件,ActionForm實例依然保存在requestsession范圍內(nèi)。

 

3.2 創(chuàng)建ActionForm

    Struts框架中定義的ActionForm類時抽象的,必需在應(yīng)用中創(chuàng)建它的子類,來捕獲具體的HTML表單數(shù)據(jù),ActionForm Bean中的屬性和HTML表單中的字段一一對應(yīng)。

 

1 validate()方法

    如果Struts的配置文件滿足以下兩個條件,Struts控制器就會調(diào)用ActionFormvalidate()方法:

· ActionForm配置了Action映射,即<form-bean>元素的name屬性和<action>元素的name屬性匹配。

· <action>元素的validate屬性為true

ActionForm基類中定義的validate()方法直接返回null,如果創(chuàng)建了擴(kuò)展ActionForm基類的子類,那么應(yīng)該在子類中覆蓋validate()方法。

validate()方法主要負(fù)責(zé)檢查數(shù)據(jù)的格式和語法,而不負(fù)責(zé)檢查數(shù)據(jù)是否符合業(yè)務(wù)邏輯。

 

2 reset()方法

    不管ActionFormj存在于哪個范圍內(nèi),對于每個請求,控制器都會先調(diào)用ActionFormreset()方法,然后再把用戶輸入的表單數(shù)據(jù)組裝到ActionForm中。reset()方法用于恢復(fù)ActionForm的屬性的默認(rèn)值,例如把boolean類型屬性設(shè)為truefalse,把字符串屬性設(shè)為null或某個初始值。

    如果ActionFormrequest范圍內(nèi),那么對于每個新的請求都會創(chuàng)建新的ActionForm實例。當(dāng)新的實例創(chuàng)建后,如果它的屬性已經(jīng)被初始化為默認(rèn)值,那么接著再在reset()方法中把屬性設(shè)為默認(rèn)值不是很有必要,因此在這種情況下,可以讓reset()方法為空。

對于session范圍內(nèi)的ActionForm,同一ActionForm實例會被多個請求共享,reset()方法在這種情況下極為有用。

 

3.3 配置ActionForm

    Struts配置文件的<form-beans>元素用來配置所有的ActionForm Bean<form-beans>元素可以包含多個<form-bean>子元素,它代表單個的ActionForm Bean

    同一個ActionForm可以和多個Action映射。在<action>元素中,namescope屬性分別指定ActionForm的名字和范圍,validate屬性指定是否執(zhí)行表單驗證。

 

3.4 訪問ActionForm

    ActionForm可以被JSPStruts標(biāo)簽、Action和其他Web組件訪問。訪問ActionForm大致有以下一些方法:

    1 使用Struts HTML標(biāo)簽庫

        Struts HTML標(biāo)簽庫提供了一組和ActionForm密切關(guān)聯(lián)的標(biāo)簽,<html:form>標(biāo)簽生成HTML表單,它包括<html:text><html:select><html:option><html:radio><html:submit>等子標(biāo)簽,這些子標(biāo)簽構(gòu)成HTML表單的字段或按鈕。<html:form>標(biāo)簽?zāi)芎?/span>ActionForm交互,讀取ActionForm的屬性值,把他們賦值給表單中對應(yīng)的字段。

2 requestsession范圍內(nèi)取出ActionForm實例

    Struts框架把ActionForm實例保存在HttpServletRequestHttpSession中,保存時采用的屬性key<form-bean>元素的name屬性。因此,如果ActionFormrequest范圍內(nèi),則可以調(diào)用HttpServletRequestgetAttribute()方法讀取ActionForm實例。如果ActionFormsession范圍內(nèi),則可以調(diào)用HttpSessiongetAttribute()方法讀取ActionForm實例。

3 Action類的execute()方法中直接訪問ActionForm

   如果配置了ActionFormAction的映射,Struts框架就會把ActionForm作為參數(shù)傳遞給Actionexecute()方法,因此在Action類的execute()方法中可以讀取或設(shè)置ActionForm屬性。

 

3.5 處理表單跨頁

有的時候,由于表單數(shù)據(jù)太多,無法在同一個頁面顯示(如用于用戶注冊的表單),可以把它拆分成多個表單,分多個頁面顯示。在這種情況下,既可以為每個表單創(chuàng)建單獨的ActionForm,頁可以只創(chuàng)建一個ActionForm,它和多個表單對應(yīng)。

(1) HTML表單拆分到多個JSP頁面中

    在兩個JSP頁面中均定義了HTML表單,由于這兩個表單都對應(yīng)同一個ActionForm,因此可以在每個表單中定義一個隱含字段<html:hidden property=”page”/>,它代表當(dāng)前頁面編號,ActionForm將通過這個字段來識別當(dāng)前正在處理的時哪個表單。

(2) 創(chuàng)建多個HTML表單對應(yīng)的ActionForm

    在創(chuàng)建這個ActionForm時有以下幾點需要注意:

· 提供和HTML表單的隱藏字段page對應(yīng)的page屬性:

private String page = null;

public String getPage() {

    return page;

}

public void setPage(String page) {

    this.page = page;

}

· reset()方法中,只能把和當(dāng)前正在處理的表單相關(guān)的屬性恢復(fù)為默認(rèn)值,否則,如果每次都把ActionForm的所有屬性恢復(fù)為默認(rèn)值,將使用戶輸入的上一頁表單數(shù)據(jù)丟失。由于Struts框架先調(diào)用reset()方法,然后再把用戶輸入的表單數(shù)據(jù)組裝到ActionForm中,因此在reset()方法中,不能根據(jù)page屬性來判斷處理的時哪個頁面,而應(yīng)該直接從HttpServletRequest對象中讀取當(dāng)前表單的page字段值:

    int numPage = new Integer(request.getParameter(“page”)).intValue();

· validate()方法中,僅對和當(dāng)前表單相關(guān)的屬性進(jìn)行也政。由于Struts框架在調(diào)用validate()方法之前,已經(jīng)把用戶輸入的表單數(shù)據(jù)組裝到ActionForm中,因此在validate()方法中可以根據(jù)page屬性決定正在處理哪個表單。

(3) 配置ActionForm和多個Action映射

    當(dāng)ActionForm與多個表單對應(yīng)時,應(yīng)該把ActionForm存放在session范圍內(nèi)。

 

4 使用動態(tài)ActionForm

Struts框架中,ActionForm對象用來包裝HTML表單數(shù)據(jù),并能動態(tài)返回用于顯示給用戶的數(shù)據(jù)。自定義的ActionForm必需符合JavaBean規(guī)范,并繼承StrutsActionForm類,同時,用戶可以有選擇地覆蓋兩個方法:reset()validate()

ActionForm的以上特性可以簡化Web應(yīng)用的開發(fā),因為它可以協(xié)助自動進(jìn)行表示層的數(shù)據(jù)驗證。ActionForm的唯一缺點時對于大型的Struts應(yīng)用,必需以編程的方式創(chuàng)建大量的ActionForm類,如果HTML表單的字段發(fā)生變化,就必需修改并重編譯相關(guān)的ActionForm類。

Struts 1.1對此做了改進(jìn),引入了動態(tài)ActionForm類的概念。Struts框架的DynaActionForm類及其子類實現(xiàn)了動態(tài)ActionFormDynaActionForm類是ActionForm類的子類。

4.1 配置動態(tài)ActionForm

    動態(tài)ActionForm支持在Struts配置文件中完成ActionForm的全部配置,沒有必要編寫額外的程序來創(chuàng)建具體的ActionForm類。配置動態(tài)ActionForm的方法為:在Struts配置文件中配置一個<form-bean>元素,將type屬性設(shè)置為DynaActionForm或它的某個子類的全名。

    <form-bean><form-property>子元素用來設(shè)置動態(tài)ActionForm的屬性。<form-property>元素的name屬性指定屬性名,type指定屬性類型,可以把動態(tài)ActionForm的屬性設(shè)為以下Java類型:

· java.lang.BigDecimal

· java.lang.BigInteger

· java.lang.Boolean

· java.lang.Byte

· java.lang.Character

· java.lang.Class

· java.lang.Double

· java.lang.Float

· java.lang.Integer

· java.lang.Long

· java.lang.Short

· java.lang.String

· java.sql.Data

· java.sql.Time

· java.sql.Timestamp

如果表單的字段值為Java基本類型,在配置時應(yīng)該用響應(yīng)的包裝類型來代替,例如int類型的包裝類型為Integer

 

4.2 動態(tài)ActionFormreset()方法

DynaActionForm基類提供了initialize()方法,它把表單的所有屬性恢復(fù)為默認(rèn)值。表單屬性默認(rèn)值由<form-bean><form-property>子元素的initial屬性來決定。如果沒有設(shè)置initial屬性,則表單屬性的默認(rèn)值由其Java類型來自動決定,例如對象類型的默認(rèn)值為null,整數(shù)類型的默認(rèn)值為0boolean類型的默認(rèn)值為false

DynaActionForm基類的initialize()方法的代碼如下:

public void initialize(ActionMapping mapping) {

    String name = mapping.getName();

    if (name == null) {

        return;

}

FormBeanConfig config =

    mapping.getModuleConfig().findFormBeanConfig(name);

if (config == null) {

    return;

}

FormPropertyConfig props[] = config.findFormPropertyConfigs();

for (int i = 0; i < props.length; i++) {

    set(props[i].getName(), props[i].initial());

}

}

DynaActionForm基類的reset()方法不執(zhí)行任何操作,其代碼如下:

public void reset(ActionMapping mapping, HttpServletRequest request) {

    ;        // Default implementation does nothing

}

如果希望Struts框架在每次把表單數(shù)據(jù)組裝到動態(tài)ActionForm中之前,先把所有的屬性恢復(fù)為默認(rèn)值,可以定義一個擴(kuò)展DynaActionForm類的子類,然后覆蓋其reset()方法,在reset()方法中只要調(diào)用initialize()方法即可,代碼如下:

public class MyDynaActionForm extends DynaActionForm {

    ……

    public void rest(ActionMapping mapping, HttpServletRequest request) {

        initialize(mapping);

}

}

 

4.3 訪問動態(tài)ActionForm

    Action類和JSP都可以訪問動態(tài)ActionForm,使用方法與標(biāo)準(zhǔn)ActionForm大致相同,只有一點小差別。如果使用標(biāo)準(zhǔn)ActionForm對象,在標(biāo)準(zhǔn)ActionForm中針對每個屬性都提供了get/set方法,來讀取或設(shè)置屬性。

    DynaActionForm把所有的屬性保存在一個Map類對象中,并提供了下面的用于訪問所有屬性的通用方法:

        public Object get(String name)

        public void set(String name, Object value)

    get(String name)方法根據(jù)指定的屬性名返回屬性值;set(String name, Object value)方法用于為給定的屬性賦值。

 

4.4 動態(tài)ActionForm的表單驗證

    DynaActionForm基類的validate()方法沒有提供任何默認(rèn)的驗證行為。可以定義擴(kuò)展DynaActionForm的子類,然后覆蓋validate()方法,但是以編程的方式來驗證動態(tài)ActionForm違背了Struts框架提供動態(tài)ActionForm的初中,即以配置來替代編程。幸運的是,可以采用另一種驗證機制,即Validator框架來完成驗證。Validator框架允許采用特定的配置文件來為動態(tài)ActionForm配置驗證規(guī)則。

 

 

5 小結(jié)

    本篇側(cè)重介紹了構(gòu)成Struts視圖的組件之一:ActionFormActionForm用于在視圖層和控制層之間傳遞表單數(shù)據(jù),ActionForm可以存放在requestsession范圍內(nèi)。ActionForm是一種Web組件,不應(yīng)該在模型層直接訪問ActionForm Bean。同一個ActionForm可以對應(yīng)多個HTML表單,在這種情況下,有以下開發(fā)技巧:

· HTML表單中定義<html:hidden property=”page” />隱藏字段,來標(biāo)識當(dāng)前頁面。

· ActionForm中定義page屬性,它和表單中的隱藏字段page對應(yīng)。

· ActionFormreset()方法中,只能把和當(dāng)前表單相關(guān)的屬性恢復(fù)為默認(rèn)值。可以調(diào)用request.getParameter(“page”)方法來讀取當(dāng)前的頁面編號。

· ActionFormvalidate()方法中,只能對和當(dāng)前表單相關(guān)的屬性進(jìn)行驗證。此時page屬性代表當(dāng)前的頁面編號。

· 在配置ActionFormAction的映射時,應(yīng)該把ActionForm的范圍設(shè)為session

 

Struts框架還印入了DynaActionForm類,它允許以配置的方式來創(chuàng)建動態(tài)ActionForm,使用DynaActionForm有以下幾點需要注意:

· <form><form-property>子元素用于配置動態(tài)ActionForm的屬性。<form-property>元素的type屬性指定ActionForm的屬性的類型。如果屬性為Java基本類型,應(yīng)該把屬性設(shè)置為相應(yīng)的Java包裝類型。

· 提倡使用Validator框架來驗證動態(tài)ActionForm,這樣可以避免以編程的方式來實現(xiàn)validate()方法。


閱讀材料:《精通Struts:基于MVC的Java Web設(shè)計與開發(fā)》





                                   2005年05月12日 6:59 PM