作為基于MVC模式的Web應用最經(jīng)典框架,Struts已經(jīng)正式推出了1.1版本,該版本在以往版本的基礎上,提供了許多激動(dòng)人心的新功能。本文就將帶你走進(jìn)Struts 1.1去深入地了解這些功能。 說(shuō)明:希望本文的讀者能有一定的Struts使用基礎。 Model 2 Struts是基于Model 2之上的,而Model 2是經(jīng)典的MVC(模型-視圖-控制器)模型的Web應用變體,這個(gè)改變主要是由于網(wǎng)絡(luò )應用的特性--HTTP協(xié)議的無(wú)狀態(tài)性引起的。Model 2的目的和MVC一樣,也是利用控制器來(lái)分離模型和視圖,達到一種層間松散耦合的效果,提高系統靈活性、復用性和可維護性。在多數情況下,你可以將Model 2與MVC等同起來(lái)。 下圖表示一個(gè)基于Java技術(shù)的典型網(wǎng)絡(luò )應用,從中可以看出Model 2中的各個(gè)部分是如何對應于Java中各種現有技術(shù)的。
在利用Model 2之前,我們是把所有的表示邏輯和業(yè)務(wù)邏輯都集中在一起(比如大雜燴似的JSP),有時(shí)也稱(chēng)這種應用模式為Model 1,Model 1的主要缺點(diǎn)就是緊耦合,復用性差以及維護成本高。
Struts 1.1 和Model 2 既然Struts 1.1是基于Model 2之上,那它的底層機制也就是MVC,下面是Struts 1.1中的MVC實(shí)現示意圖:
圖解說(shuō)明:其中不同顏色代表MVC的不同部分:紅色(控制器)、紫色(模型)和綠色(視圖) 首先,控制器(ActionServlet)進(jìn)行初始化工作,讀取配置文件(struts-config.xml),為不同的Struts模塊初始化相應的ModuleConfig對象。比如配置文件中的Action映射定義都保存在A(yíng)ctionConfig集合中。相應地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和MessageResourcesConfig集合等。 提示:模塊是在Struts 1.1中新提出的概念,在稍后的內容中我們將詳細介紹,你現在可以簡(jiǎn)單地把模塊看作是一個(gè)子系統,它們共同組成整個(gè)應用,同時(shí)又各自獨立。Struts 1.1中所有的處理都是在特定模塊環(huán)境中進(jìn)行的。模塊的提出主要是為了解決Struts 1.0中單配置文件的問(wèn)題。 控制器接收HTTP請求,并從ActionConfig中找出對應于該請求的Action子類(lèi),如果沒(méi)有對應的Action,控制器直接將請求轉發(fā)給JSP或者靜態(tài)頁(yè)面。否則控制器將請求分發(fā)至具體Action類(lèi)進(jìn)行處理。 在控制器調用具體Action的execute方法之前,ActionForm對象將利用HTTP請求中的參數來(lái)填充自己(可選步驟,需要在配置文件中指定)。具體的ActionForm對象應該是ActionForm的子類(lèi)對象,它其實(shí)就是一個(gè)JavaBean。此外,還可以在A(yíng)ctionForm類(lèi)中調用validate方法來(lái)檢查請求參數的合法性,并且可以返回一個(gè)包含所有錯誤信息的ActionErrors對象。如果執行成功,ActionForm自動(dòng)將這些參數信息以JavaBean(一般稱(chēng)之為form bean)的方式保存在Servlet Context中,這樣它們就可以被其它Action對象或者JSP調用。 Struts將這些ActionForm的配置信息都放在FormBeanConfig集合中,通過(guò)它們Struts能夠知道針對某個(gè)客戶(hù)請求是否需要創(chuàng )建相應的ActionForm實(shí)例。 Action很簡(jiǎn)單,一般只包含一個(gè)execute方法,它負責執行相應的業(yè)務(wù)邏輯,如果需要,它也進(jìn)行相應的數據檢查。執行完成之后,返回一個(gè)ActionForward對象,控制器通過(guò)該ActionForward對象來(lái)進(jìn)行轉發(fā)工作。我們主張將獲取數據和執行業(yè)務(wù)邏輯的功能放到具體的JavaBean當中,而Action只負責完成與控制有關(guān)的功能。遵循該原則,所以在上圖中我將Action對象歸為控制器部分。 提示:其實(shí)在Struts 1.1中,ActionMapping的作用完全可以由ActionConfig來(lái)替代,只不過(guò)由于它是公共API的一部分以及兼容性的問(wèn)題得以保留。ActionMapping通過(guò)繼承ActionConfig來(lái)獲得與其一致的功能,你可以等同地看待它們。同理,其它例如ActionForward與ForwardConfig的關(guān)系也是如此。 下圖給出了客戶(hù)端從發(fā)出請求到獲得響應整個(gè)過(guò)程的圖解說(shuō)明。
下面我們就來(lái)詳細地討論一下其中的每個(gè)部分,在這之前,先來(lái)了解一下模塊的概念。
模塊 我們知道,在Struts 1.0中,我們只能在web.xml中為ActionServlet指定一個(gè)配置文件,這對于我們這些網(wǎng)上的教學(xué)例子來(lái)說(shuō)當然沒(méi)什么問(wèn)題,但是在實(shí)際的應用開(kāi)發(fā)過(guò)程中,可能會(huì )有些麻煩。因為許多開(kāi)發(fā)人員都可能同時(shí)需要修改配置文件,但是配置文件只能同時(shí)被一個(gè)人修改,這樣肯定會(huì )造成一定程度上的資源爭奪,勢必會(huì )影響開(kāi)發(fā)效率和引起開(kāi)發(fā)人員的抱怨。 在Struts 1.1中,為了解決這個(gè)并行開(kāi)發(fā)的問(wèn)題,提出了兩種解決方案: - 多個(gè)配置文件的支持
- 模塊的支持
支持多個(gè)配置文件,是指你能夠為ActionServlet同時(shí)指定多個(gè)xml配置文件,文件之間以逗號分隔,比如Struts提供的MailReader演示例子中就采用該種方法。 <!-- Action Servlet Configuration --> <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, /WEB-INF/struts-config-registration.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> | 這種方法可以很好地解決修改沖突的問(wèn)題,不同的開(kāi)發(fā)人員可以在不同的配置文件中設置自己的Action、ActionForm等等(當然不是說(shuō)每個(gè)開(kāi)發(fā)人員都需要自己的配置文件,可以按照系統的功能模塊進(jìn)行劃分)。但是,這里還是存在一個(gè)潛在的問(wèn)題,就是可能不同的配置文件之間會(huì )產(chǎn)生沖突,因為在A(yíng)ctionServlet初始化的時(shí)候這幾個(gè)文件最終還是需要合并到一起的。比如,在struts-config.xml中配置了一個(gè)名為success的<forward>,而在struts-config-registration.xml中也配置了一個(gè)同樣的<forward>,那么執行起來(lái)就會(huì )產(chǎn)生沖突。 為了徹底解決這種沖突,Struts 1.1中引進(jìn)了模塊(Module)的概念。一個(gè)模塊就是一個(gè)獨立的子系統,你可以在其中進(jìn)行任意所需的配置,同時(shí)又不必擔心和其它的配置文件產(chǎn)生沖突。因為前面我們講過(guò),ActionServlet是將不同的模塊信息保存在不同的ModuleConfig對象中的。要使用模塊的功能,需要進(jìn)行以下的準備工作: 1、為每個(gè)模塊準備一個(gè)配置文件 2、配置web.xml文件,通知控制器 決定采用多個(gè)模塊以后,你需要將這些信息告訴控制器,這需要在web.xml文件進(jìn)行配置。下面是一個(gè)典型的多模塊配置: <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/customer</param-name> <param-value>/WEB-INF/struts-config-customer.xml</param-value> </init-param> <init-param> <param-name>config/order</param-name> <param-value>/WEB-INF/struts-config-order.xml</param-value> </init-param> | 要配置多個(gè)模塊,你需要在原有的一個(gè)<init-param>(在Struts 1.1中將其對應的模塊稱(chēng)為缺省模塊)的基礎之上,增加模塊對應的<init-param>。其中<param-name>表示為config/XXX的形式,其中XXX為對應的模塊名,<param-value>中還是指定模塊對應的配置文件。上面這個(gè)例子說(shuō)明該應用有三個(gè)模塊,分別是缺省模塊、customer和order,它們分別對應不同的配置文件。 3、準備各個(gè)模塊所需的ActionForm、Action和JSP等資源 但是要注意的是,模塊的出現也同時(shí)帶來(lái)了一個(gè)問(wèn)題,即如何在不同模塊間進(jìn)行轉發(fā)?有兩種方法可以實(shí)現模塊間的轉發(fā),一種就是在<forward>(全局或者本地)中定義,另外一種就是利用org.apache.struts.actions.SwitchAction。 下面就是一個(gè)全局的例子: ... <struts-config> ... <global-forwards> <forward name="toModuleB" contextRelative="true" path="/moduleB/index.do" redirect="true"/> ... </global-forwards> ... </struts-config> | 可以看出,只需要在原有的path屬性前加上模塊名,同時(shí)將contextRelative屬性置為true即可。此外,你也可以在<action>中定義一個(gè)類(lèi)似的本地<forward>。 <action-mappings> <!-- Action mapping for profile form --> <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin" validate="true"> <forward name="success" contextRelative="true" path="/moduleA/login.do"/> </action> </action-mappings> | 如果你已經(jīng)處在其他模塊,需要轉回到缺省模塊,那應該類(lèi)似下面這樣定義,即模塊名為空。 <forward name="success" contextRelative="true" path="/login.do"/> | 此外,你也可以使用org.apache.struts.actions.SwitchAction,例如: ... <action-mappings> <action path="/toModule" type="org.apache.struts.actions.SwitchAction"/> ... </action-mappings> ... |
ActionServlet 我們首先來(lái)了解MVC中的控制器。在Struts 1.1中缺省采用ActionServlet類(lèi)來(lái)充當控制器。當然如果ActionServlet不能滿(mǎn)足你的需求,你也可以通過(guò)繼承它來(lái)實(shí)現自己的類(lèi)。這可以在/WEB-INF/web.xml中來(lái)具體指定。 要掌握ActionServlet,就必須了解它所扮演的角色。首先,ActionServlet表示MVC結構中的控制器部分,它需要完成控制器所需的前端控制及轉發(fā)請求等職責。其次,ActionServlet被實(shí)現為一個(gè)專(zhuān)門(mén)處理HTTP請求的Servlet,它同時(shí)具有servlet的特點(diǎn)。在Struts 1.1中它主要完成以下功能: - 接收客戶(hù)端請求
- 根據客戶(hù)端的URI將請求映射到一個(gè)相應的Action類(lèi)
- 從請求中獲取數據填充Form Bean(如果需要)
- 調用Action類(lèi)的execute()方法獲取數據或者執行業(yè)務(wù)邏輯
- 選擇正確的視圖響應客戶(hù)
此外,ActionServlet還負責初始化和清除應用配置信息的任務(wù)。ActionServlet的初始化工作在init方法中完成,它可以分為兩個(gè)部分:初始化ActionServlet自身的一些信息以及每個(gè)模塊的配置信息。前者主要通過(guò)initInternal、initOther和initServlet三個(gè)方法來(lái)完成。 我們可以在/WEB-INF/web.xml中指定具體的控制器以及初始參數,由于版本的變化以及Struts 1.1中模塊概念的引進(jìn),一些初始參數被廢棄或者移入到/WEB-INF/struts-config.xml中定義。下面列出所有被廢棄的參數,相應地在web.xml文件中也不鼓勵再使用。 - application
- bufferSize
- content
- debug
- factory
- formBean
- forward
- locale
- mapping
- maxFileSize
- multipartClass
- nocache
- null
- tempDir
ActionServlet根據不同的模塊來(lái)初始化ModuleConfig類(lèi),并在其中以XXXconfig集合的方式保存該模塊的各種配置信息,比如ActionConfig,FormBeanConfig等。 初始化工作完成之后,ActionServlet準備接收客戶(hù)請求。針對每個(gè)請求,方法process(HttpServletRequest request, HttpServletResponse response)將被調用。該方法指定具體的模塊,然后調用該模塊的RequestProcessor的process方法。 protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestUtils.selectModule(request, getServletContext()); getRequestProcessor(getModuleConfig(request)).process(request, response); } | RequestProcessor包含了Struts控制器的所有處理邏輯,它調用不同的processXXX方法來(lái)完成不同的處理。下表列出其中幾個(gè)主要的方法: | 方法 | 功能 | | processPath | 獲取客戶(hù)端的請求路徑 | | processMapping | 利用路徑來(lái)獲得相應的ActionMapping | | processActionForm | 初始化ActionForm(如果需要)并存入正確的scope中 | | processActionCreate | 初始化Action | | processActionPerform | 調用Action的execute方法 | | processForwardConfig | 處理Action返回的ActionForward |
ActionForm 對于A(yíng)ctionForm你可以從以下幾個(gè)方面來(lái)理解它: - ActionForm表示HTTP窗體中的數據,可以將其看作是模型和視圖的中介,它負責保存視圖中的數據供模型或者視圖使用。Struts 1.1文檔中把它比作HTTP和Action之間的防火墻,這體現了ActionForm具有的過(guò)濾保護的作用,只有通過(guò)ActionForm驗證的數據才能夠發(fā)送到Action處理。
- ActionForm是與一個(gè)或多個(gè)ActionConfig關(guān)聯(lián)的JavaBean,在相應的action的execute方法被調用之前,ActionForm會(huì )自動(dòng)利用請求參數來(lái)填充自己(初始化屬性)。
- ActionForm是一個(gè)抽象類(lèi),你必須通過(guò)繼承來(lái)實(shí)現自己的類(lèi)。
ActionForm首先利用屬性的getter和setter方法來(lái)實(shí)現初始化,初始化完畢后,ActionForm的validate方法被調用,你可以在其中來(lái)檢查請求參數的正確性和有效性,并且可以將錯誤信息以ActionErrors的形式返回到輸入窗體。否則,ActionForm將被作為參數傳給action的execute方法以供使用。 ActionForm bean的生命周期可以設置為session(缺?。┖蛂equest,當設置為session時(shí),記得在reset方法中將所有的屬性重新設置為初始值。 由于A(yíng)ctionForm對應于HTTP窗體,所以隨著(zhù)頁(yè)面的增多,你的ActionForm將會(huì )急速增加。而且可能同一類(lèi)型頁(yè)面字段將會(huì )在不同的ActionForm中出現,并且在每個(gè)ActionForm中都存在相同的驗證代碼。為了解決這個(gè)問(wèn)題,你可以為整個(gè)應用實(shí)現一個(gè)ActionForm或者至少一個(gè)模塊對應于一個(gè)ActionForm。 但是,聚合的代價(jià)就是復用性很差,而且難維護。針對這個(gè)問(wèn)題,在Struts 1.1中提出了DynaActionForm的概念。 DynaActionForm類(lèi) DynaActionForm的目的就是減少ActionForm的數目,利用它你不必創(chuàng )建一個(gè)個(gè)具體的ActionForm類(lèi),而是在配置文件中配置出所需的虛擬ActionForm。例如,在下表中通過(guò)指定<form-bean>的type為"org.apache.struts.action.DynaActionForm"來(lái)創(chuàng )建一個(gè)動(dòng)態(tài)的ActionForm--loginForm。 <form-beans> <form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="actionClass" type="java.lang.String"/> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> | 動(dòng)態(tài)的ActionForm的使用方法跟普通的ActionForm相同,但是要注意一點(diǎn)。普通的ActionForm對象需要為每個(gè)屬性提供getter和setter方法,以上面的例子而言,我們需要提供getUsername() 和 setUsername()方法取得和設置username屬性,同樣地有一對方法用于取得和設置password屬性和actionClass屬性。 如果使用DynaActionForm,它將屬性保存在一個(gè)HashMap類(lèi)對象中,同時(shí)提供相應的get(name) 和 set(name)方法,其中參數name是要訪(fǎng)問(wèn)的屬性名。例如要訪(fǎng)問(wèn)DynaActionForm中username的值,可以采用類(lèi)似的代碼: String username = (String)form.get("username"); | 由于值存放于一個(gè)HashMap對象,所以要記得對get()方法返回的Object對象做強制性類(lèi)型轉換。正是由于這點(diǎn)區別,如果你在A(yíng)ction中非常頻繁地使用ActionForm對象,建議還是使用普通的ActionForm對象。 在Struts 1.1中,除了DynaActionForm以外,還提供了表單輸入自動(dòng)驗證的功能,在包org.apache.struts.validator中提供了許多有用的類(lèi),其中最常見(jiàn)的就是DynaValidatorForm類(lèi)。 DynaValidatorForm類(lèi) DynaValidatorForm是DynaActionForm的子類(lèi),它能夠提供動(dòng)態(tài)ActionForm和自動(dòng)表單輸入驗證的功能。和使用DynaActionForm類(lèi)似,你必須首先在配置文件中進(jìn)行配置: <form-beans> <form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="actionClass" type="java.lang.String"/> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> | 同時(shí)要定義驗證的插件: <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> | 其中的validator.xml和validator-rules.xml分別表示驗證定義和驗證規則的內容(可以合并在一起),比如針對上例中的DynaValidatorForm,我們有如下驗證定義(validator.xml): <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <!-- Validation Rules $Id: validation.xml--> <form-validation> <!-- ========== Default Language Form Definitions ===================== --> <formset> <form name="loginForm"> <field property="username" depends="required, minlength,maxlength"> <arg0 key="prompt.username"/> <arg1 key="${var:minlength}" name="minlength" resource="false"/> <arg2 key="${var:maxlength}" name="maxlength" resource="false"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> <var> <var-name>minlength</var-name> <var-value>3</var-value> </var> </field> <field property="password" depends="required, minlength,maxlength" bundle="alternate"> <arg0 key="prompt.password"/> <arg1 key="${var:minlength}" name="minlength" resource="false"/> <arg2 key="${var:maxlength}" name="maxlength" resource="false"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> <var> <var-name>minlength</var-name> <var-value>3</var-value> </var> </field> </form> </formset> </form-validation> | 從上述定義中,我們可以看到對于字段username有三項驗證:required, minlength, maxlength,意思是該字段不能為空,而且長(cháng)度在3和16之間。而validator-rules.xml文件則可以采用Struts提供的缺省文件。注意在<form-bean>中定義的form是如何與validation.xml中的form關(guān)聯(lián)起來(lái)的。最后,要啟動(dòng)自動(dòng)驗證功能,還需要將Action配置的validate屬性設置為true。 <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin"validate="true"> | 此時(shí),Struts將根據xml配置文件中的定義來(lái)檢驗表單輸入,并將不符合要求的錯誤信息輸出到頁(yè)面。但是你可能會(huì )想:這個(gè)功能雖然好,可是什么檢驗都跑到服務(wù)器端執行,效率方面和用戶(hù)易用性方面是不是有些問(wèn)題?你可能會(huì )懷念起那簡(jiǎn)單的JavaScript客戶(hù)端驗證。 不用擔心,在Struts 1.1中也支持JavaScript客戶(hù)端驗證。如果你選擇了客戶(hù)端驗證,當某個(gè)表單被提交以后,Struts 1.1啟動(dòng)客戶(hù)端驗證,如果瀏覽器不支持JavaScript驗證,則服務(wù)器端驗證被啟動(dòng),這種雙重驗證機制能夠最大限度地滿(mǎn)足各種開(kāi)發(fā)者的需要。JavaScript驗證代碼也是在validator-rules.xml文件中定義的。要啟動(dòng)客戶(hù)端驗證,你必須在相應的JSP文件中做如下設置: - 為<html:form>增加onsubmit屬性
- 設置Javascript支持
下表中列出了一JSP文件的示例代碼,紅字部分為Javascript驗證所需代碼。 <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <table bgcolor="#9AFF9A" cellspacing="0" cellpadding="10" border="1" width="100%"> <tr> <td> <table cellspacing="0" cellpadding="0" border="0" width="100%"> <tr bgcolor="#696969"> <td align="center"> <font color="#FFFFFF">Panel 3: Profile</font> </td> </tr> <tr> <td><br> <html:errors/> <html:form action="/login.do" focus="username" onsubmit="return validateLoginForm(this);"> <html:hidden property="actionClass"/> <center> <table> <tr> <td>UserName:</td> <td><html:text property="username" size="20"/></td> </tr> <tr> <td>Password:</td> <td><html:password property="password" size="20"/></td> </tr> <tr> <td colspan=2><html:submit property="submitProperty" value="Submit"/></td> </table> </center> </html:form> <html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false"/> <script language="Javascript1.1" src="staticJavascript.jsp"></script> </td> </tr> </table> </td> </tr> </table> | 其中onsubmit的值為"return validateLoginForm(this);",它的語(yǔ)法為: return validate + struts-config.xml中定義的form-bean名稱(chēng) + (this); staticJavascript.jsp的內容為: <%@ page language="java" %> <%-- set document type to Javascript (addresses a bug in Netscape according to a web resource --%> <%@ page contentType="application/x-javascript" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:javascript dynamicJavascript="false" staticJavascript="true"/> | 如果validator-rules.xml中定義的基本驗證功能不能滿(mǎn)足你的需求,你可以自己添加所需的驗證類(lèi)型。
Action 我們通過(guò)繼承Action類(lèi)來(lái)實(shí)現具體的執行類(lèi)。具體Action類(lèi)的功能一般都在execute(以前是perform方法)方法中完成,其中主要涉及到以下幾個(gè)方面: - 輔助ActionForm進(jìn)行一些表單數據的檢查。
- 執行必要的業(yè)務(wù)邏輯,比如存取數據庫,調用實(shí)體bean等。
- 更新服務(wù)器端的bean數據,后續對象中可能會(huì )用到這些數據,比如在JSP中利用bean:write來(lái)獲得這些數據。
- 根據處理結果決定程序的去處,并以ActionForward對象的形式返回給ActionServlet。
提示:由于在A(yíng)ction和ActionForm中都可以實(shí)現驗證方法,那么如何來(lái)安排它們之間的分工呢?一般來(lái)說(shuō),我們秉著(zhù)MVC分離的原則,也就是視圖級的驗證工作放在A(yíng)ctionForm來(lái)完成,比如輸入不能為空,email格式是否正確,利用ValidatorForm可以很輕松地完成這些工作。而與具體業(yè)務(wù)相關(guān)的驗證則放入Action中,這樣就可以獲得最大ActionForm重用性的可能。 前面我們提到過(guò),我們主張將業(yè)務(wù)邏輯執行分離到單獨的JavaBean中,而Action只負責錯誤處理和流程控制。而且考慮到重用性的原因,在執行業(yè)務(wù)邏輯的JavaBean中不要引用任何與Web應用相關(guān)的對象,比如HttpServletRequest,HttpServletResponse等對象,而應該將其轉化為普通的Java對象。關(guān)于這一點(diǎn),可以參考Petstore中WAF框架的實(shí)現思路。 此外,你可能還注意到execute與perform的一個(gè)區別:execute方法簡(jiǎn)單地擲出Exception異常,而perform方法則擲出ServletException和IOException異常。這不是說(shuō)Struts 1.1在異常處理功能方面弱化了,而是為了配合Struts 1.1中一個(gè)很好的功能--宣稱(chēng)式異常處理機制。
宣稱(chēng)式異常處理 和EJB中的宣稱(chēng)式事務(wù)處理概念類(lèi)似,宣稱(chēng)式異常處理其實(shí)就是可配置的異常處理,你可以在配置文件中指定由誰(shuí)來(lái)處理Action類(lèi)中擲出的某種異常。你可以按照以下步驟來(lái)完成該功能: - 實(shí)現org.apache.struts.action.ExceptionHandler的子類(lèi),覆蓋execute方法,在該方法中處理異常并且返回一個(gè)ActionForward對象
- 在配置文件中配置異常處理對象,你可以配置一個(gè)全局的處理類(lèi)或者單獨為每個(gè)Action配置處理類(lèi)
下表就定義了一個(gè)全局的處理類(lèi)CustomizedExceptionHandler,它被用來(lái)處理所有的異常。 <global-exceptions> <exception handler="com.yourcorp.CustomizedExceptionHandler" key="global.error.message" path="/error.jsp" scope="request" type="java.lang.Exception"/> </global-exceptions> | 其中具體的參數含義,可以參考ExceptionHandler.java源文件。
taglib 講完了模型和控制器,接下來(lái)我們要涉及的是視圖。視圖的角色主要是由JSP來(lái)完成,從JSP的規范中可以看出,在視圖層可以"折騰"的技術(shù)不是很多,主要的就是自定義標記庫的應用。Struts 1.1在原有的四個(gè)標記庫的基礎上新增了兩個(gè)標記庫--Tiles和Nested。 其中Tiles除了替代Template的基本模板功能外,還增加了布局定義、虛擬頁(yè)面定義和動(dòng)態(tài)頁(yè)面生成等功能。Tiles強大的模板功能能夠使頁(yè)面獲得最大的重用性和靈活性,此外可以結合Tiles配置文件中的頁(yè)面定義和Action的轉發(fā)邏輯,即你可以將一個(gè)Action轉發(fā)到一個(gè)在Tiles配置文件中定義的虛擬頁(yè)面,從而減少頁(yè)面的數量。比如,下表中的Action定義了一個(gè)轉發(fā)路徑,它的終點(diǎn)是tile.userMain,而后者是你在Tiles配置文件中定義的一個(gè)頁(yè)面。 <!-- ========== Action Mapping Definitions ============================== --> <action-mappings> <!-- Action mapping for profile form --> <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin" validate="true"> <forward name="success" path="tile.userMain"/> </action> </action-mappings> | Tiles配置文件:tiles-defs.xml <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration//EN" "http://jakarta.apache.org/struts/dtds/tiles-config.dtd"> <tiles-definitions> <!-- ======================================================= --> <!-- Master definitions --> <!-- ======================================================= --> <!-- Page layout used as root for all pages. --> <definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp"> <put name="titleString" value="CHANGE-ME"/> <put name="topMenu" value="/tiles-components/topMenu.jsp"/> <put name="leftMenu" value="/tiles-components/panel1.jsp"/> <put name="body" value="CHANGE-ME"/> <put name="footer" value="/tiles-components/footer.jsp"/> </definition> <!-- ======================================================= --> <!-- Page definitions --> <!-- ======================================================= --> <!-- User Login page --> <definition name="tile.userLogin" extends="rootLayout"> <put name="titleString" value="User Login"/> <put name="body" value="/src/userLogin.jsp"/> </definition> <!-- User Main page --> <definition name="tile.userMain" extends="rootLayout"> <put name="titleString" value="User Main"/> <put name="body" value="/src/userMain.jsp"/> </definition> </tiles-definitions> | 而Nested標記庫的作用是讓以上這些基本標記庫能夠嵌套使用,發(fā)揮更大的作用。
Commons Logging 接口 所謂的Commons Logging接口,是指將日志功能的使用與日志具體實(shí)現分開(kāi),通過(guò)配置文件來(lái)指定具體使用的日志實(shí)現。這樣你就可以在Struts 1.1中通過(guò)統一的接口來(lái)使用日志功能,而不去管具體是利用的哪種日志實(shí)現,有點(diǎn)于類(lèi)似JDBC的功能。Struts 1.1中支持的日志實(shí)現包括:Log4J,JDK Logging API, LogKit,NoOpLog和SimpleLog。 你可以按照如下的方式來(lái)使用Commons Logging接口(可以參照Struts源文中的許多類(lèi)實(shí)現): package com.foo; // ... import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; //... public class Foo { // ... private static Log log = LogFactory.getLog(Foo.class); // ... public void setBar(Bar bar) { if (log.isTraceEnabled()) { log.trace("Setting bar to " + bar); } this.bar = bar; } // ... } | 而開(kāi)啟日志功能最簡(jiǎn)單的辦法就是在WEB-INF/classes目錄下添加以下兩個(gè)文件: commons-logging.properties文件: # Note: The Tiles framework now uses the commons-logging package to output different information or debug statements. Please refer to this package documentation to enable it. The simplest way to enable logging is to create two files in WEB-INF/classes: # commons-logging.properties # org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog # simplelog.properties # # Logging detail level, # # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). #org.apache.commons.logging.simplelog.defaultlog=trace org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog | simplelog.properties文件: # Logging detail level, # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). org.apache.commons.logging.simplelog.defaultlog=fatal | 這里我們采用的日志實(shí)現是SimpleLog,你可以在simplelog.properties文件指定日志明細的級別:trace,debug,info,warn,error和fatal,從trace到fatal錯誤級別越來(lái)越高,同時(shí)輸出的日志信息也越來(lái)越少。而這些級別是和org.apache.commons.logging.log接口中的方法一一對應的。這些級別是向后包含的,也就是前面的級別包含后面級別的信息。 |