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)簽
· JavaScript和stylesheet
·
多媒體文件
·
消息資源(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框架提供的DTO:ActionForm Bean
ActionForm Bean是Struts框架提供的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有兩種存在范圍:request和session。如果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的映射,控制器將從request或session范圍中取出ActionForm實例,如果該實例不存在,就會自動創(chuàng)建一個新的實例。當(dāng)控制器接受到一個新的請求時,ActionForm的生命周期如下:
· 控制器接收到請求
·
從request或session范圍中取出ActionForm實例,如果該實例不存在,就自動創(chuàng)建一個新的實例。
·
調(diào)用ActionForm的reset()方法
·
把ActionForm實例保存在request或session范圍中
·
把用戶輸入的表單數(shù)據(jù)組裝到ActionForm中
·
如果<action>的validate屬性為true,則調(diào)用ActionForm的validate()方法。
· (1) 如果存在驗證錯誤,把請求轉(zhuǎn)發(fā)給<action>的input屬性指定的Web組件,ActionForm實例依然保持在request或session范圍內(nèi)。
(2) 如果無驗證錯誤,調(diào)用Action的execute()方法,把ActionForm實例傳遞給execute()方法。
·
把請求轉(zhuǎn)發(fā)給其他Web組件,ActionForm實例依然保存在request或session范圍內(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)用ActionForm的validate()方法:
· 為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)用ActionForm的reset()方法,然后再把用戶輸入的表單數(shù)據(jù)組裝到ActionForm中。reset()方法用于恢復(fù)ActionForm的屬性的默認(rèn)值,例如把boolean類型屬性設(shè)為true或false,把字符串屬性設(shè)為null或某個初始值。
如果ActionForm在request范圍內(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>元素中,name和scope屬性分別指定ActionForm的名字和范圍,validate屬性指定是否執(zhí)行表單驗證。
3.4 訪問ActionForm
ActionForm可以被JSP、Struts標(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 從request或session范圍內(nèi)取出ActionForm實例
Struts框架把ActionForm實例保存在HttpServletRequest或HttpSession中,保存時采用的屬性key為<form-bean>元素的name屬性。因此,如果ActionForm在request范圍內(nèi),則可以調(diào)用HttpServletRequest的getAttribute()方法讀取ActionForm實例。如果ActionForm在session范圍內(nèi),則可以調(diào)用HttpSession的getAttribute()方法讀取ActionForm實例。
3 在Action類的execute()方法中直接訪問ActionForm
如果配置了ActionForm的Action的映射,Struts框架就會把ActionForm作為參數(shù)傳遞給Action的execute()方法,因此在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ī)范,并繼承Struts的ActionForm類,同時,用戶可以有選擇地覆蓋兩個方法: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)ActionForm,DynaActionForm類是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)ActionForm的reset()方法
DynaActionForm基類提供了initialize()方法,它把表單的所有屬性恢復(fù)為默認(rèn)值。表單屬性默認(rèn)值由<form-bean>的<form-property>子元素的initial屬性來決定。如果沒有設(shè)置initial屬性,則表單屬性的默認(rèn)值由其Java類型來自動決定,例如對象類型的默認(rèn)值為null,整數(shù)類型的默認(rèn)值為0,boolean類型的默認(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視圖的組件之一:ActionForm。ActionForm用于在視圖層和控制層之間傳遞表單數(shù)據(jù),ActionForm可以存放在request和session范圍內(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)。
·
在ActionForm的reset()方法中,只能把和當(dāng)前表單相關(guān)的屬性恢復(fù)為默認(rèn)值。可以調(diào)用request.getParameter(“page”)方法來讀取當(dāng)前的頁面編號。
·
在ActionForm的validate()方法中,只能對和當(dāng)前表單相關(guān)的屬性進(jìn)行驗證。此時page屬性代表當(dāng)前的頁面編號。
·
在配置ActionForm和Action的映射時,應(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