Struts視圖組件

Struts框架的視圖負責為客戶提供動態網頁內容。Struts視圖主要由JSP網頁構成,此外,Struts框架還提供了Struts客戶化標簽和ActionForm Bean,這些組件提供對國際化、接收用戶輸入的表單數據、表單驗證和錯誤處理等的支持,使開發者可以把更多的經歷放在實現業務需求上。

 

1 視圖概述

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

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

· HTML文檔

· JSP客戶化標簽

· JavaScriptstylesheet

· 多媒體文件

· 消息資源(Resource Bundle

· ActionForm Bean

 

2 在視圖中使用JavaBean

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

· 必需提供不帶參數的構造方法。

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

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

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

 

2,1 DTO數據傳輸對象

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

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

· 減少傳輸數據的冗余,提高傳輸效率。

· 有助于實現各個層之間的獨立,使每個層分工明確。模型層負責業務邏輯,視圖層負責向用戶戰士模型狀態。采用DTO,模型層對視圖層屏蔽了業務邏輯細節,向視圖層提供可以直接現實給用戶的數據。

 

2.2 Struts框架提供的DTOActionForm Bean

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

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

 

3 使用ActionForm

 

3.1 使用ActionForm

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

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

· 控制器接收到請求

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

· 調用ActionFormreset()方法

· ActionForm實例保存在requestsession范圍中

· 把用戶輸入的表單數據組裝到ActionForm

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

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

 

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

           · 把請求轉發給其他Web組件,ActionForm實例依然保存在requestsession范圍內。

 

3.2 創建ActionForm

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

 

1 validate()方法

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

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

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

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

validate()方法主要負責檢查數據的格式和語法,而不負責檢查數據是否符合業務邏輯。

 

2 reset()方法

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

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

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

 

3.3 配置ActionForm

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

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

 

3.4 訪問ActionForm

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

    1 使用Struts HTML標簽庫

        Struts HTML標簽庫提供了一組和ActionForm密切關聯的標簽,<html:form>標簽生成HTML表單,它包括<html:text><html:select><html:option><html:radio><html:submit>等子標簽,這些子標簽構成HTML表單的字段或按鈕。<html:form>標簽能和ActionForm交互,讀取ActionForm的屬性值,把他們賦值給表單中對應的字段。

2 requestsession范圍內取出ActionForm實例

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

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

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

 

3.5 處理表單跨頁

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

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

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

(2) 創建多個HTML表單對應的ActionForm

    在創建這個ActionForm時有以下幾點需要注意:

· 提供和HTML表單的隱藏字段page對應的page屬性:

private String page = null;

public String getPage() {

    return page;

}

public void setPage(String page) {

    this.page = page;

}

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

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

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

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

    ActionForm與多個表單對應時,應該把ActionForm存放在session范圍內。

 

4 使用動態ActionForm

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

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

Struts 1.1對此做了改進,引入了動態ActionForm類的概念。Struts框架的DynaActionForm類及其子類實現了動態ActionFormDynaActionForm類是ActionForm類的子類。

4.1 配置動態ActionForm

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

    <form-bean><form-property>子元素用來設置動態ActionForm的屬性。<form-property>元素的name屬性指定屬性名,type指定屬性類型,可以把動態ActionForm的屬性設為以下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基本類型,在配置時應該用響應的包裝類型來代替,例如int類型的包裝類型為Integer

 

4.2 動態ActionFormreset()方法

DynaActionForm基類提供了initialize()方法,它把表單的所有屬性恢復為默認值。表單屬性默認值由<form-bean><form-property>子元素的initial屬性來決定。如果沒有設置initial屬性,則表單屬性的默認值由其Java類型來自動決定,例如對象類型的默認值為null,整數類型的默認值為0boolean類型的默認值為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()方法不執行任何操作,其代碼如下:

public void reset(ActionMapping mapping, HttpServletRequest request) {

    ;        // Default implementation does nothing

}

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

public class MyDynaActionForm extends DynaActionForm {

    ……

    public void rest(ActionMapping mapping, HttpServletRequest request) {

        initialize(mapping);

}

}

 

4.3 訪問動態ActionForm

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

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

        public Object get(String name)

        public void set(String name, Object value)

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

 

4.4 動態ActionForm的表單驗證

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

 

 

5 小結

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

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

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

· ActionFormreset()方法中,只能把和當前表單相關的屬性恢復為默認值。可以調用request.getParameter(“page”)方法來讀取當前的頁面編號。

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

· 在配置ActionFormAction的映射時,應該把ActionForm的范圍設為session

 

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

· <form><form-property>子元素用于配置動態ActionForm的屬性。<form-property>元素的type屬性指定ActionForm的屬性的類型。如果屬性為Java基本類型,應該把屬性設置為相應的Java包裝類型。

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


閱讀材料:《精通Struts:基于MVC的Java Web設計與開發》





                                   2005年05月12日 6:59 PM