Struts視圖組件
Struts框架的視圖負責為客戶提供動態網頁內容。Struts視圖主要由JSP網頁構成,此外,Struts框架還提供了Struts客戶化標簽和ActionForm Bean,這些組件提供對國際化、接收用戶輸入的表單數據、表單驗證和錯誤處理等的支持,使開發者可以把更多的經歷放在實現業務需求上。
1 視圖概述
視圖使模型的外在表現形式,用戶通過視圖來了解模型的狀態。同一個模型可以有多種視圖。
在Struts框架中,視圖主要由JSP組件構成,此外,視圖還可以包含以下組件:
· HTML文檔
· JSP客戶化標簽
· JavaScript和stylesheet
·
多媒體文件
·
消息資源(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框架提供的DTO:ActionForm Bean
ActionForm Bean是Struts框架提供的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有兩種存在范圍:request和session。如果ActionForm存在于request范圍,它僅在當前的請求/響應生命周期中有效。在請求從一個Web組件轉發到另一個Web組件的過程中,ActionForm實例一直有效。當服務器把響應結果返回給客戶,ActionForm實例及其包含的數據就會被銷毀。如果ActionForm存在于session范圍,同一個ActionForm實例在整個HTTP會話中有效。
當控制器接受到請求時,如果請求訪問的Web組件為Action,并且為這個Action配置了和ActionForm的映射,控制器將從request或session范圍中取出ActionForm實例,如果該實例不存在,就會自動創建一個新的實例。當控制器接受到一個新的請求時,ActionForm的生命周期如下:
· 控制器接收到請求
·
從request或session范圍中取出ActionForm實例,如果該實例不存在,就自動創建一個新的實例。
·
調用ActionForm的reset()方法
·
把ActionForm實例保存在request或session范圍中
·
把用戶輸入的表單數據組裝到ActionForm中
·
如果<action>的validate屬性為true,則調用ActionForm的validate()方法。
· (1) 如果存在驗證錯誤,把請求轉發給<action>的input屬性指定的Web組件,ActionForm實例依然保持在request或session范圍內。
(2) 如果無驗證錯誤,調用Action的execute()方法,把ActionForm實例傳遞給execute()方法。
·
把請求轉發給其他Web組件,ActionForm實例依然保存在request或session范圍內。
3.2 創建ActionForm
Struts框架中定義的ActionForm類時抽象的,必需在應用中創建它的子類,來捕獲具體的HTML表單數據,ActionForm
Bean中的屬性和HTML表單中的字段一一對應。
1 validate()方法
如果Struts的配置文件滿足以下兩個條件,Struts控制器就會調用ActionForm的validate()方法:
· 為ActionForm配置了Action映射,即<form-bean>元素的name屬性和<action>元素的name屬性匹配。
·
<action>元素的validate屬性為true。
在ActionForm基類中定義的validate()方法直接返回null,如果創建了擴展ActionForm基類的子類,那么應該在子類中覆蓋validate()方法。
validate()方法主要負責檢查數據的格式和語法,而不負責檢查數據是否符合業務邏輯。
2 reset()方法
不管ActionFormj存在于哪個范圍內,對于每個請求,控制器都會先調用ActionForm的reset()方法,然后再把用戶輸入的表單數據組裝到ActionForm中。reset()方法用于恢復ActionForm的屬性的默認值,例如把boolean類型屬性設為true或false,把字符串屬性設為null或某個初始值。
如果ActionForm在request范圍內,那么對于每個新的請求都會創建新的ActionForm實例。當新的實例創建后,如果它的屬性已經被初始化為默認值,那么接著再在reset()方法中把屬性設為默認值不是很有必要,因此在這種情況下,可以讓reset()方法為空。
對于session范圍內的ActionForm,同一ActionForm實例會被多個請求共享,reset()方法在這種情況下極為有用。
3.3 配置ActionForm
Struts配置文件的<form-beans>元素用來配置所有的ActionForm Bean。<form-beans>元素可以包含多個<form-bean>子元素,它代表單個的ActionForm Bean。
同一個ActionForm可以和多個Action映射。在<action>元素中,name和scope屬性分別指定ActionForm的名字和范圍,validate屬性指定是否執行表單驗證。
3.4 訪問ActionForm
ActionForm可以被JSP、Struts標簽、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 從request或session范圍內取出ActionForm實例
Struts框架把ActionForm實例保存在HttpServletRequest或HttpSession中,保存時采用的屬性key為<form-bean>元素的name屬性。因此,如果ActionForm在request范圍內,則可以調用HttpServletRequest的getAttribute()方法讀取ActionForm實例。如果ActionForm在session范圍內,則可以調用HttpSession的getAttribute()方法讀取ActionForm實例。
3 在Action類的execute()方法中直接訪問ActionForm
如果配置了ActionForm的Action的映射,Struts框架就會把ActionForm作為參數傳遞給Action的execute()方法,因此在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規范,并繼承Struts的ActionForm類,同時,用戶可以有選擇地覆蓋兩個方法:reset()和validate()。
ActionForm的以上特性可以簡化Web應用的開發,因為它可以協助自動進行表示層的數據驗證。ActionForm的唯一缺點時對于大型的Struts應用,必需以編程的方式創建大量的ActionForm類,如果HTML表單的字段發生變化,就必需修改并重編譯相關的ActionForm類。
Struts 1.1對此做了改進,引入了動態ActionForm類的概念。Struts框架的DynaActionForm類及其子類實現了動態ActionForm,DynaActionForm類是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 動態ActionForm的reset()方法
DynaActionForm基類提供了initialize()方法,它把表單的所有屬性恢復為默認值。表單屬性默認值由<form-bean>的<form-property>子元素的initial屬性來決定。如果沒有設置initial屬性,則表單屬性的默認值由其Java類型來自動決定,例如對象類型的默認值為null,整數類型的默認值為0,boolean類型的默認值為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視圖的組件之一:ActionForm。ActionForm用于在視圖層和控制層之間傳遞表單數據,ActionForm可以存放在request和session范圍內。ActionForm是一種Web組件,不應該在模型層直接訪問ActionForm Bean。同一個ActionForm可以對應多個HTML表單,在這種情況下,有以下開發技巧:
·
在HTML表單中定義<html:hidden property=”page” />隱藏字段,來標識當前頁面。
·
在ActionForm中定義page屬性,它和表單中的隱藏字段page對應。
·
在ActionForm的reset()方法中,只能把和當前表單相關的屬性恢復為默認值。可以調用request.getParameter(“page”)方法來讀取當前的頁面編號。
·
在ActionForm的validate()方法中,只能對和當前表單相關的屬性進行驗證。此時page屬性代表當前的頁面編號。
·
在配置ActionForm和Action的映射時,應該把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