第一個(gè)Struts 應(yīng)用:helloapp
應(yīng)用 本章講解了一個(gè)簡(jiǎn)單的Struts應(yīng)用例子——helloapp 應(yīng)用,這個(gè)例子可以幫助讀者迅速 入門,,獲得開(kāi)發(fā)Struts 應(yīng)用的基本經(jīng)驗(yàn),。該應(yīng)用的功能非常簡(jiǎn)單:接受用戶輸入的姓名 <name>,然后輸出“Hello <name>”,。開(kāi)發(fā)helloapp應(yīng)用涉及以下內(nèi)容: l 分析應(yīng)用需求 l 把基于MVC設(shè)計(jì)模式的Struts框架運(yùn)用到應(yīng)用中 l 創(chuàng)建視圖組件,,包括HTML表單(hello.jsp)和ActionForm Bean(HelloForm.java) l 創(chuàng)建application.properties資源文件 l 數(shù)據(jù)驗(yàn)證,包括表單驗(yàn)證和業(yè)務(wù)邏輯驗(yàn)證 l 創(chuàng)建控制器組件:HelloAction.java l 創(chuàng)建模型組件:PersonBean.java l 創(chuàng)建包含被各個(gè)模塊共享的常量數(shù)據(jù)的Java 文件:Constants.java l 創(chuàng)建配置文件:web.xml 和struts-config.xml l 編譯,、發(fā)布和運(yùn)行helloapp應(yīng)用 2.1 分析helloapp 應(yīng)用的需求 在開(kāi)發(fā)應(yīng)用時(shí),,首先從分析需求入手,列舉該應(yīng)用的各種功能,,以及限制條件,。helloapp 應(yīng)用的需求非常簡(jiǎn)單,其包括如下需求: l 接受用戶輸入的姓名<name>,,然后返回字符串“Hello <name> !”,。 l 如果用戶沒(méi)有輸入姓名就提交表單,將返回出錯(cuò)信息,,提示用戶首先輸入姓名,。 l 如果用戶輸入姓名為“Monster”,將返回出錯(cuò)信息,,拒絕向“Monster”打招呼,。 l 為了演示模型組件的功能,本應(yīng)用使用模型組件來(lái)保存用戶輸入的姓名,。 2.2 運(yùn)用Struts 框架 下面把Struts框架運(yùn)用到helloapp應(yīng)用中,。Struts框架可以方便迅速地把一個(gè)復(fù)雜的應(yīng) 用劃分成模型,、視圖和控制器組件,,而Struts的配置文件struts-config.xml 則可以靈活地組 裝這些組件,簡(jiǎn)化開(kāi)發(fā)過(guò)程,。 以下是helloapp應(yīng)用的各個(gè)模塊的構(gòu)成: PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 20 l 模型包括一個(gè)JavaBean 組件PersonBean,,它有一個(gè)userName 屬性,代表用戶輸 入的名字。它提供了get/set 方法,,分別用于讀取和設(shè)置userName 屬性,,它還提 供一個(gè)save()方法,負(fù)責(zé)把userName屬性保存到持久化存儲(chǔ)系統(tǒng)中,,如數(shù)據(jù)庫(kù)或 文件系統(tǒng),。對(duì)于更為復(fù)雜的Web 應(yīng)用,JavaBean組件可以作為EJB 或Web 服務(wù) 的前端組件,。 l 視圖包括一個(gè)JSP 文件hello.jsp,,它提供用戶界面,接受用戶輸入的姓名,。視圖 還包括一個(gè)ActionForm Bean,,它用來(lái)存放表單數(shù)據(jù),并進(jìn)行表單驗(yàn)證,,如果用戶 沒(méi)有輸入姓名就提交表單,,將返回出錯(cuò)信息。 l 控制器包括一個(gè)Action類HelloAction,,它完成三項(xiàng)任務(wù):一是進(jìn)行業(yè)務(wù)邏輯驗(yàn)證,, 如果用戶輸入的姓名為“Monster”,將返回錯(cuò)誤消息,;二是調(diào)用模型組件 PersonBean 的save()方法,,保存用戶輸入的名字;三是決定將合適的視圖組件返 回給用戶,。 除了創(chuàng)建模型,、視圖和控制器組件,還需要?jiǎng)?chuàng)建Struts 的配置文件struts-config.xml,, 它可以把這些組件組裝起來(lái),,使它們協(xié)調(diào)工作。此外,,還需要?jiǎng)?chuàng)建整個(gè)Web應(yīng)用的配置文 件web.xml,。 2.3 創(chuàng)建視圖組件 在本例中,視圖包括兩個(gè)組件: l 一個(gè)JSP文件:hello.jsp,。 l 一個(gè)ActionForm Bean:HelloForm Bean,。 下面分別講述如何創(chuàng)建這兩個(gè)組件。 2.3.1 創(chuàng)建JSP文件 hello.jsp 提供用戶界面,,能夠接受用戶輸入的姓名,。此外,本W(wǎng)eb 應(yīng)用的所有輸出結(jié) 果也都由hello.jsp顯示給用戶,。圖2-1顯示了hello.jsp提供的網(wǎng)頁(yè),。 圖2-1 hello.jsp的網(wǎng)頁(yè) 在圖2-1中,,當(dāng)用戶輸入姓名“Weiqin”后,單擊【Submit】按鈕提交表單,,本應(yīng)用將 返回“Hello Weiqin!”,,參見(jiàn)圖2-2。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 21 圖2-2 hello.jsp接受用戶輸入后正常返回的網(wǎng)頁(yè) 例程2-1為hello.jsp文件的源代碼,。 例程2-1 hello.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html:html locale="true"> <head> <title><bean:message key="hello.jsp.title"/></title> <html:base/> </head> <body bgcolor="white"><p> <h2><bean:message key="hello.jsp.page.heading"/></h2><p> <html:errors/><p> <logic:present name="personbean" scope="request"> <h2> <bean:message key="hello.jsp.page.hello"/> <bean:write name="personbean" property="userName" />!<p> </h2> </logic:present> <html:form action="/HelloWorld.do" focus="userName" > <bean:message key="hello.jsp.prompt.person"/> <html:text property="userName" size="16" maxlength="16"/><br> <html:submit property="submit" value="Submit"/> <html:reset/> </html:form><br> PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 22 <html:img page="/struts-power.gif" alt="Powered by Struts"/> </body> </html:html> 以上基于Struts框架的JSP文件有以下特點(diǎn): l 沒(méi)有任何Java 程序代碼,。 l 使用了許多Struts的客戶化標(biāo)簽,例如<html:form>和<logic:present>標(biāo)簽,。 l 沒(méi)有直接提供文本內(nèi)容,,取而代之的是<bean:message>標(biāo)簽,輸出到網(wǎng)頁(yè)上的文 本內(nèi)容都是由<bean:message>標(biāo)簽來(lái)生成的,。例如: <bean:message key="hello.jsp.prompt.person"/> Struts客戶化標(biāo)簽是聯(lián)系視圖組件和Struts框架中其他組件的紐帶,。這些標(biāo)簽可以訪問(wèn) 或顯示來(lái)自于控制器和模型組件的數(shù)據(jù)。在本書(shū)第12 章至第16 章將專門介紹Struts 標(biāo)簽 的用法,,本節(jié)先簡(jiǎn)單介紹幾種重要的Struts標(biāo)簽,。 hello.jsp開(kāi)頭幾行用于聲明和加載Struts標(biāo)簽庫(kù): <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> 以上代碼表明該JSP文件使用了Struts Bean、Html 和Logic 標(biāo)簽庫(kù),,這是加載客戶化 標(biāo)簽庫(kù)的標(biāo)準(zhǔn)JSP語(yǔ)法,。 hello.jsp中使用了來(lái)自 Struts HTML標(biāo)簽庫(kù)中的標(biāo)簽,包括<html:errors>, <html:form> 和<html:text>: l <html:errors>:用于顯示Struts框架中其他組件產(chǎn)生的錯(cuò)誤消息,。 l <html:form>:用于創(chuàng)建HTML 表單,,它能夠把HTML 表單的字段和ActionForm Bean的屬性關(guān)聯(lián)起來(lái)。 l <html:text>:該標(biāo)簽是<html:form>的子標(biāo)簽,,用于創(chuàng)建HTML表單的文本框,。它 和ActionForm Bean的屬性相關(guān)聯(lián)。 hello.jsp中使用了來(lái)自Struts Bean標(biāo)簽庫(kù)的兩個(gè)標(biāo)簽<bean:message>和<bean:write>: l <bean:message>:用于輸出本地化的文本內(nèi)容,,它的key屬性指定消息key,,與消 息key匹配的文本內(nèi)容來(lái)自于專門的Resource Bundle,關(guān)于Resource Bundle的概 念參見(jiàn)本書(shū)第9 章(Struts應(yīng)用的國(guó)際化),。 l <bean:write>:用于輸出JavaBean 的屬性值,。本例中,它用于輸出personbean 對(duì) 象的userName屬性值: <bean:write name="personbean" property="userName" /> hello.jsp 使用了來(lái)自Struts Logic 標(biāo)簽庫(kù)的<logic:present>標(biāo)簽,。<logic:present>標(biāo)簽用 來(lái)判斷JavaBean 在特定的范圍內(nèi)是否存在,,只有當(dāng)JavaBean 存在時(shí),才會(huì)執(zhí)行標(biāo)簽主體 中的內(nèi)容: <logic:present name="personbean" scope="request"> <h2> PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 23 Hello <bean:write name="personbean" property="userName" />!<p> </h2> </logic:present> 在本例中,,<logic:present>標(biāo)簽用來(lái)判斷在request 范圍內(nèi)是否存在personbean 對(duì)象,, 如果存在,就輸出personbean 的userName 屬性值,。與<logic:present>標(biāo)簽相對(duì)的是 <logic:notPresent>標(biāo)簽,,它表示只有當(dāng)JavaBean 在特定的范圍內(nèi)不存在時(shí),,才會(huì)執(zhí)行標(biāo)簽 主體中的內(nèi)容,。 2.3.2 創(chuàng)建消息資源文件 hello.jsp使用<bean:message>標(biāo)簽來(lái)輸出文本內(nèi)容,。這些文本來(lái)自于Resource Bundle, 每個(gè)Resource Bundle 都對(duì)應(yīng)一個(gè)或多個(gè)本地化的消息資源文件,,本例中的資源文件為 application.properties,,例程2-2是該消息資源文件的內(nèi)容。 例程2-2 application.properties文件 #Application Resources for the "Hello" sample application hello.jsp.title=Hello - A first Struts program hello.jsp.page.heading=Hello World! A first Struts application hello.jsp.prompt.person=Please enter a UserName to say hello to : hello.jsp.page.hello=Hello #Validation and error messages for HelloForm.java and HelloAction.java hello.dont.talk.to.monster=We don‘t want to say hello to Monster!!! hello.no.username.error=Please enter a <i>UserName</i> to say hello to! 以上文件以“消息key/消息文本”的格式存放數(shù)據(jù),,文件中“#”的后面為注釋行,。對(duì) 于以下JSP代碼: <bean:message key="hello.jsp.title"/> <bean:message>標(biāo)簽的key 屬性為“hello.jsp.tilte”,在Resource Bundle 中與之匹配的 內(nèi)容為: hello.jsp.title=Hello - A first Struts program 因此,,以上<bean:message>標(biāo)簽將把“Hello - A first Struts program”輸出到網(wǎng)頁(yè)上,。 2.3.3 創(chuàng)建ActionForm Bean 當(dāng)用戶提交了HTML 表單后,Struts 框架將自動(dòng)把表單數(shù)據(jù)組裝到ActionForm Bean 中,。ActionForm Bean中的屬性和HTML表單中的字段一一對(duì)應(yīng),。ActionForm Bean還提供 數(shù)據(jù)驗(yàn)證方法,以及把屬性重新設(shè)置為默認(rèn)值的方法,。Struts 框架中定義的ActionForm 類 是抽象的,,必須在應(yīng)用中創(chuàng)建它的子類,來(lái)存放具體的HTML 表單數(shù)據(jù),。例程2-3 為 HelloForm.java 的源程序, 它用于處理hello.jsp中的表單數(shù)據(jù),。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 24 例程2-3 HelloForm.java package hello; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public final class HelloForm extends ActionForm { private String userName = null; public String getUserName() { return (this.userName); } public void setUserName(String userName) { this.userName = userName; } /** * Reset all properties to their default values. */ public void reset(ActionMapping mapping, HttpServletRequest request) { this.userName = null; } /** * Validate the properties posted in this request. If validation errors are * found, return an <code>ActionErrors</code> object containing the errors. * If no validation errors occur, return <code>null</code> or an empty * <code>ActionErrors</code> object. */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((userName == null) || (userName.length() < 1)) errors.add("username", new ActionMessage("hello.no.username.error")); return errors; } } PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 25 從以上代碼中可以看出,ActionForm Bean 實(shí)質(zhì)上是一種JavaBean,,不過(guò)它除了具有 JavaBean的常規(guī)方法,,還有兩種特殊方法: l validate():用于表單驗(yàn)證。 l reset():把屬性重新設(shè)置為默認(rèn)值,。 2.3.4 數(shù)據(jù)驗(yàn)證 幾乎所有和用戶交互的應(yīng)用都需要數(shù)據(jù)驗(yàn)證,,而從頭設(shè)計(jì)并開(kāi)發(fā)完善的數(shù)據(jù)驗(yàn)證機(jī)制 往往很費(fèi)時(shí)。幸運(yùn)的是,,Struts框架提供了現(xiàn)成的,、易于使用的數(shù)據(jù)驗(yàn)證功能。Struts框架 的數(shù)據(jù)驗(yàn)證可分為兩種類型:表單驗(yàn)證和業(yè)務(wù)邏輯驗(yàn)證,,在本例中,,它們分別運(yùn)用于以下 場(chǎng)合: l 表單驗(yàn)證:如果用戶沒(méi)有在表單中輸入姓名就提交表單,將生成表單驗(yàn)證錯(cuò)誤,。 l 業(yè)務(wù)邏輯驗(yàn)證:如果用戶在表單中輸入的姓名為“Monster”,,按照本應(yīng)用的業(yè)務(wù) 規(guī)則,即不允許向“Monster”打招呼,,因此將生成業(yè)務(wù)邏輯錯(cuò)誤,。 第一種類型的驗(yàn)證,,即表單驗(yàn)證由ActionForm Bean 來(lái)負(fù)責(zé)處理。在本例中,, HelloForm.java 的validate()方法負(fù)責(zé)完成這一任務(wù): public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((userName == null) || (userName.length() < 1)) errors.add("username", new ActionMessage("hello.no.username.error")); return errors; } } 當(dāng)用戶提交了HTML 表單后,,Struts 框架將自動(dòng)把表單數(shù)據(jù)組裝到ActionForm Bean 中。接下來(lái)Struts 框架會(huì)自動(dòng)調(diào)用ActionForm Bean 的validate()方法進(jìn)行表單驗(yàn)證,。如果 validate()方法返回的ActionErrors 對(duì)象為null,,或者不包含任何ActionMessage對(duì)象,就表 示沒(méi)有錯(cuò)誤,,數(shù)據(jù)驗(yàn)證通過(guò),。如果ActionErrors中包含ActionMessage對(duì)象,就表示發(fā)生了 驗(yàn)證錯(cuò)誤,,Struts 框架會(huì)把ActionErrors 對(duì)象保存到request 范圍內(nèi),,然后把請(qǐng)求轉(zhuǎn)發(fā)到恰 當(dāng)?shù)囊晥D組件,視圖組件通過(guò)<html:errors>標(biāo)簽把request 范圍內(nèi)的ActionErrors 對(duì)象中包 含的錯(cuò)誤消息顯示出來(lái),,提示用戶修改錯(cuò)誤,。 在Struts 早期的版本中使用ActionError 類來(lái)表示錯(cuò)誤消息, ActionError 類是ActionMessage的子類,。Struts 1.2 將廢棄ActionError,,統(tǒng) 一采用ActionMessage類來(lái)表示正常或錯(cuò)誤消息,。 第二種類型的驗(yàn)證,,即業(yè)務(wù)邏輯驗(yàn)證由Action來(lái)負(fù)責(zé)處理,參見(jiàn)本章的2.4.3 小節(jié),。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 26 2.4 創(chuàng)建控制器組件 控制器組件包括ActionServlet 類和Action 類,。ActionServlet 類是Struts 框架自帶的, 它是整個(gè)Struts 框架的控制樞紐,,通常不需要擴(kuò)展,。Struts 框架提供了可供擴(kuò)展的Action 類,它用來(lái)處理特定的HTTP請(qǐng)求,,例程2-4為HelloAction類的源程序,。 例程2-4 HelloAction.java package hello; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.util.MessageResources; public final class HelloAction extends Action { /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. */ public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // These "messages" come from the ApplicationResources.properties file MessageResources messages = getResources(request); /* * Validate the request parameters specified by the user PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 27 * Note: Basic field validation done in HelloForm.java * Business logic validation done in HelloAction.java */ ActionMessages errors = new ActionMessages(); String userName = (String)((HelloForm) form).getUserName(); String badUserName = "Monster"; if (userName.equalsIgnoreCase(badUserName)) { errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName )); saveErrors(request, errors); return (new ActionForward(mapping.getInput())); } /* * Having received and validated the data submitted * from the View, we now update the model */ PersonBean pb = new PersonBean(); pb.setUserName(userName); pb.saveToPersistentStore(); /* * If there was a choice of View components that depended on the model * (or some other) status, we‘d make the decision here as to which * to display. In this case, there is only one View component. * * We pass data to the View components by setting them as attributes * in the page, request, session or servlet context. In this case, the * most appropriate scoping is the "request" context since the data * will not be neaded after the View is generated. * * Constants.PERSON_KEY provides a key accessible by both the * Controller component (i.e. this class) and the View component * (i.e. the jsp file we forward to). */ request.setAttribute( Constants.PERSON_KEY, pb); // Remove the Form Bean - don‘t need to carry values forward request.removeAttribute(mapping.getAttribute()); // Forward control to the specified success URI return (mapping.findForward("SayHello")); } PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 28 } HelloAction.java 是本應(yīng)用中最復(fù)雜的程序,下面分步講解它的工作機(jī)制和流程,。 2.4.1 Action類的工作機(jī)制 所有的Action類都是org.apache.struts.action.Action的子類,。Action子類應(yīng)該覆蓋父類 的execute()方法。當(dāng)ActionForm Bean 被創(chuàng)建,,并且表單驗(yàn)證順利通過(guò)后,, Struts 框架就 會(huì)調(diào)用Action類的execute()方法。execute()方法的定義如下: public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException ,; execute()方法包含以下參數(shù): l ActionMapping:包含了這個(gè)Action 的配置信息,,和struts-config.xml 文件中的 <action>元素對(duì)應(yīng),。 l ActionForm:包含了用戶的表單數(shù)據(jù),當(dāng)Struts 框架調(diào)用execute()方法時(shí),, ActionForm中的數(shù)據(jù)已經(jīng)通過(guò)了表單驗(yàn)證,。 l HttpServletRequest:當(dāng)前的HTTP請(qǐng)求對(duì)象。 l HttpServletResponse:當(dāng)前的HTTP響應(yīng)對(duì)象,。 Action類的execute()方法返回ActionForward對(duì)象,,它包含了請(qǐng)求轉(zhuǎn)發(fā)路徑信息,。 2.4.2 訪問(wèn)封裝在MessageResources中的本地化文本 在本例中,,Action類的execute()方法首先獲得MessageResources 對(duì)象: MessageResources messages = getResources(request); 在Action類中定義了getResources(HttpServletRequest request)方法,該方法返回當(dāng)前默 認(rèn)的MessageResources 對(duì)象,,它封裝了Resource Bundle 中的文本內(nèi)容,。接下來(lái)Action類 就可以通過(guò)MessageResources 對(duì)象來(lái)訪問(wèn)文本內(nèi)容。例如,,如果要讀取消息key 為 “hello.jsp.title”對(duì)應(yīng)的文本內(nèi)容,,可以調(diào)用MessageResources 類的getMessage(String key) 方法: String title=messages.getMessage("hello.jsp.title"); 2.4.3 業(yè)務(wù)邏輯驗(yàn)證 接下來(lái),Action類的execute()方法執(zhí)行業(yè)務(wù)邏輯驗(yàn)證: ActionMessages errors = new ActionMessages(); String userName = (String)((HelloForm) form).getUserName(); String badUserName = "Monster"; if (userName.equalsIgnoreCase(badUserName)) { PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 29 errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName )); saveErrors(request, errors); return (new ActionForward(mapping.getInput())); } 如果用戶輸入的姓名為“Monster”,,將創(chuàng)建包含錯(cuò)誤信息的ActionMessage 對(duì)象,, ActionMessage 對(duì)象被保存到ActionMessages 對(duì)象中。接下來(lái)調(diào)用在Action 基類中定義的 saveErrors()方法,,它負(fù)責(zé)把ActionMessages 對(duì)象保存到request 范圍內(nèi),。最后返回 ActionForward對(duì)象,Struts框架會(huì)根據(jù)ActionForward對(duì)象包含的轉(zhuǎn)發(fā)信息把請(qǐng)求轉(zhuǎn)發(fā)到恰 當(dāng)?shù)囊晥D組件,,視圖組件通過(guò)<html:errors>標(biāo)簽把request 范圍內(nèi)的ActionMessages 對(duì)象中 包含的錯(cuò)誤消息顯示出來(lái),,提示用戶修改錯(cuò)誤。 在2.3.4小節(jié)中還提到了ActionErrors對(duì)象,。圖2-3顯示了ActionMessages,、ActionErrors、 ActionMessage 和ActionError 類的類框圖,。ActionErrors 繼承ActionMessages,,ActionError 繼承ActionMessage,ActionMessages 和ActionMessage 之間為聚集關(guān)系,,即一個(gè) ActionMessages 對(duì)象中可以包含多個(gè)ActionMessage對(duì)象,。(圖中的0..n表述是否正確?) 圖2-3 ActionMessages,、ActionErrors,、ActionMessage和ActionError 類的類框圖 表單驗(yàn)證通常只對(duì)用戶輸入的數(shù)據(jù)進(jìn)行簡(jiǎn)單的語(yǔ)法和格式檢查,而業(yè)務(wù)邏輯驗(yàn)證會(huì)對(duì) 數(shù)據(jù)進(jìn)行更為復(fù)雜的驗(yàn)證,。在很多情況下,,需要模型組件的介入,,才能完成業(yè)務(wù)邏輯驗(yàn)證。 2.4.4 訪問(wèn)模型組件 接下來(lái),,HelloAction 類創(chuàng)建了一個(gè)模型組件PersonBean 對(duì)象,,并調(diào)用它的 saveTopersistentStore()saveToPersistentStore()方法來(lái)保存userName屬性: PersonBean pb = new PersonBean(); pb.setUserName(userName); pb.saveToPersistentStore(); 本例僅提供了Action類訪問(wèn)模型組件簡(jiǎn)單的例子。在實(shí)際應(yīng)用中,,Action類會(huì)訪問(wèn)模 型組件,,完成更加復(fù)雜的功能,例如: l 從模型組件中讀取數(shù)據(jù),,用于被視圖組件顯示,。 l 和多個(gè)模型組件交互。 l 依據(jù)從模型組件中獲得的信息,,來(lái)決定返回哪個(gè)視圖組件,。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 30 2.4.5 向視圖組件傳遞數(shù)據(jù) Action 類把數(shù)據(jù)存放在request 或session 范圍內(nèi),以便向視圖組件傳遞信息,。以下是 HelloAction.java 向視圖組件傳遞數(shù)據(jù)的代碼: request.setAttribute( Constants.PERSON_KEY, pb); // Remove the Form Bean - don‘t need to carry values forward request.removeAttribute(mapping.getAttribute()); 以上代碼完成兩件事: l 把PersonBean對(duì)象保存在request范圍內(nèi),。 l 從request范圍內(nèi)刪除ActionForm Bean。由于后續(xù)的請(qǐng)求轉(zhuǎn)發(fā)目標(biāo)組件不再需要 HelloForm Bean,,所以可將它刪除,。 2.4.6 把HTTP請(qǐng)求轉(zhuǎn)發(fā)給合適的視圖組件 最后,Action類把流程轉(zhuǎn)發(fā)給合適的視圖組件,。 // Forward control to the specified success URI return (mapping.findForward("SayHello")); 2.5 創(chuàng)建模型組件 在2.4 節(jié)中已經(jīng)講過(guò),,Action 類會(huì)訪問(wèn)模型組件。本例中模型組件為JavaBean: PersonBean,。例程2-5是PersonBean的源代碼,。 例程2-5 PersonBean.java package hello; public class PersonBean { private String userName = null; public String getUserName() { return this.userName; } public void setUserName(String userName) { this.userName = userName; } /** * This is a stub method that would be used for the Model to save * the information submitted to a persistent store. In this sample * application it is not used. */ PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 31 public void saveToPersistentStore() { /* * This is a stub method that might be used to save the person‘s * name to a persistent store(i.e. database) if this were a real application. * * The actual business operations that would exist within a Model * component would depend upon the requirements of the application. */ } } PersonBean是一個(gè)非常簡(jiǎn)單的JavaBean,它包括一個(gè)userName屬性,,以及相關(guān)的get/set 方法,。此外,它還有一個(gè)業(yè)務(wù)方法saveToPersistentStore(),。本例中并沒(méi)有真正實(shí)現(xiàn)這一方 法,。在實(shí)際應(yīng)用中,這個(gè)方法可以用來(lái)把JavaBean的屬性保存在持久化存儲(chǔ)系統(tǒng)中,,如數(shù) 據(jù)庫(kù)或文件系統(tǒng),。 通過(guò)這個(gè)簡(jiǎn)單的例子,讀者可以進(jìn)一步理解Struts 框架中使用模型組件的一大優(yōu)點(diǎn),, 它把業(yè)務(wù)邏輯的實(shí)現(xiàn)和應(yīng)用的其他部分分離開(kāi)來(lái),,可以提高整個(gè)應(yīng)用的靈活性、可重用性 和可擴(kuò)展性。如果模型組件的實(shí)現(xiàn)發(fā)生改變,,例如本來(lái)把JavaBean 的屬性保存在MySQL 數(shù)據(jù)庫(kù)中,,后來(lái)改為保存在Oracle數(shù)據(jù)庫(kù)中,此時(shí)Action類不需要做任何變動(dòng),。不僅如此,, 即使模型組件由JavaBean改為EJB,運(yùn)行在遠(yuǎn)程應(yīng)用服務(wù)器上,,也不會(huì)對(duì)Action類造成任 何影響,。 2.6 創(chuàng)建存放常量的Java文件 根據(jù)2.4.5 小節(jié),HelloAction類和視圖組件之間通過(guò)HttpServletRequest的setAttribute() 和getAttribute()方法來(lái)共享request 范圍內(nèi)的數(shù)據(jù),。下面再看一下HelloAction 類調(diào)用 HttpServletRequest的setAttribute()方法的細(xì)節(jié),。 當(dāng)HelloAction 類調(diào)用HttpServletRequest 的setAttribute()方法,向hello.jsp 傳遞 PersonBean對(duì)象時(shí),,需要提供一個(gè)名為“personbean”的屬性key: request.setAttribute("personbean",pb); hello.jsp通過(guò)這個(gè)名為“personbean”的屬性key來(lái)讀取PersonBean對(duì)象: <logic:present name="personbean" scope="request"> <h2> Hello <bean:write name="personbean" property="userName" />!<p> </h2> </logic:present> 對(duì)于Struts應(yīng)用,,提倡將這些屬性key常量定義在一個(gè)Java 文件Constants.java 中,,例 程2-6顯示了它的源程序,。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 32 例程2-6 Constants.java package hello; public final class Constants { /** * The application scope attribute under which our user database * is stored. */ public static final String PERSON_KEY = "personbean"; } 這樣,HelloAction類可以按以下方式來(lái)調(diào)用HttpServletRequest的setAttribute()方法: request.setAttribute( Constants.PERSON_KEY, pb); 把一些常量定義在Constants.java 中可以提高Action類的獨(dú)立性,。當(dāng)屬性key常量值發(fā) 生改變時(shí),,只需要修改Constants.java 文件,而不需要修改Action類,。 此外,,本例把PersonBean對(duì)象保存在HttpServletRequest對(duì)象中。對(duì)于其他實(shí)際的Web 應(yīng)用,,也可以根據(jù)需要把JavaBean對(duì)象保存在HttpSession對(duì)象中,。 2.7 創(chuàng)建配置文件 2.7.1 創(chuàng)建Web應(yīng)用的配置文件 對(duì)于Struts 應(yīng)用,它的配置文件web.xml 應(yīng)該對(duì)ActionServlet 類進(jìn)行配置,。此外,,還 應(yīng)該聲明Web 應(yīng)用所使用的Struts 標(biāo)簽庫(kù),本例中聲明使用了三個(gè)標(biāo)簽庫(kù):Struts Bean,、 Struts HTML和Struts Logic標(biāo)簽庫(kù),。例程2-7為web.xml的源代碼。 例程2-7 web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java./j2ee/dtds/web-app_2_2.dtd"> <web-app> <display-name>HelloApp Struts Application</display-name> <!-- Standard 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</param-value> PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 33 </init-param> <load-on-startup>2</load-on-startup> </servlet> <!-- Standard Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Usual Welcome File List --> <welcome-file-list> <welcome-file>hello.jsp</welcome-file> </welcome-file-list> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/struts-logic.tld</taglib-location> </taglib> </web-app> 2.7.2 創(chuàng)建Struts框架的配置文件 正如前面提及的,,Struts框架允許把應(yīng)用劃分成多個(gè)組件,,提高開(kāi)發(fā)速度。而Struts框 架的配置文件struts-config.xml可以把這些組件組裝起來(lái),,決定如何使用它們,。例程2-8是 helloapp應(yīng)用的struts-config.xml文件的源代碼。 例程2-8 struts-config.xml <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta./struts/dtds/struts-config_1_1.dtd"> PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 34 <!-- This is the Struts configuration file for the "Hello!" sample application --> <struts-config> <!-- ======== Form Bean Definitions =================================== --> <form-beans> <form-bean name="HelloForm" type="hello.HelloForm"/> </form-beans> <!-- ========== Action Mapping Definitions ============================== --> <action-mappings> <!-- Say Hello! --> <action path = "/HelloWorld" type = "hello.HelloAction" name = "HelloForm" scope = "request" validate = "true" input = "/hello.jsp" > <forward name="SayHello" path="/hello.jsp" /> </action> </action-mappings> <!-- ========== Message Resources Definitions =========================== --> <message-resources parameter="hello.application"/> </struts-config> 以上代碼對(duì)helloapp 應(yīng)用的HelloForm、HelloAction 和消息資源文件進(jìn)行了配置,,首 先通過(guò)<form-bean>元素配置了一個(gè)ActionForm Bean,,名叫HelloForm,它對(duì)應(yīng)的類為 hello.HelloForm: <form-bean name="HelloForm" type="hello.HelloForm"/> 接著通過(guò)<action>元素配置了一個(gè)Action組件: <action path = "/HelloWorld" type = "hello.HelloAction" name = "HelloForm" scope = "request" validate = "true" input = "/hello.jsp" > <forward name="SayHello" path="/hello.jsp" /> </action> PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 35 <action>元素的path屬性指定請(qǐng)求訪問(wèn)Action的路徑,,type屬性指定Action的完整類 名,,name屬性指定需要傳遞給Action的ActionForm Bean,scope屬性指定ActionForm Bean 的存放范圍,,validate 屬性指定是否執(zhí)行表單驗(yàn)證,,input 屬性指定當(dāng)表單驗(yàn)證失敗時(shí)的轉(zhuǎn) 發(fā)路徑。<action>元素還包含一個(gè)<forward>子元素,,它定義了一個(gè)請(qǐng)求轉(zhuǎn)發(fā)路徑,。 本例中的<action>元素配置了HelloAction組件,對(duì)應(yīng)的類為hello.HelloAction,,請(qǐng)求訪 問(wèn)路徑為“HelloWorld”,,當(dāng)Action 類被調(diào)用時(shí),Struts 框架應(yīng)該把已經(jīng)包含表單數(shù)據(jù)的 HelloForm Bean傳給它,。HelloForm Bean存放在request范圍內(nèi),,并且在調(diào)用Action類之前, 應(yīng)該進(jìn)行表單驗(yàn)證,。如果表單驗(yàn)證失敗,,請(qǐng)求將被轉(zhuǎn)發(fā)到接收用戶輸入的網(wǎng)頁(yè)hello.jsp, 讓用戶糾正錯(cuò)誤,。 struts-config.xml文件最后通過(guò)<message-resources>元素定義了一個(gè)Resource Bundle: <message-resources parameter="hello.application"/> <message-resources>元素的parameter 屬性指定Resource Bundle使用的消息資源文件,。 本例中parameter 屬性為“hello.application”,表明消息資源文件名為“application.properties”,, 它的存放路徑為WEB-INF/classes/hello/application.properties,。 2.8 發(fā)布和運(yùn)行helloapp 應(yīng)用 helloapp應(yīng)用作為Java Web應(yīng)用,它的目錄結(jié)構(gòu)應(yīng)該符合Sun公司制定的Java Web 應(yīng) 用的規(guī)范,,此外,,由于helloapp應(yīng)用使用了Struts框架,因此應(yīng)該把Struts框架所需的JAR 文件和標(biāo)簽庫(kù)描述文件TLD 文件包含進(jìn)來(lái),。訪問(wèn)http://jakarta./builds,,可以下載 最新的Struts 軟件包,把struts 壓縮文件解壓后,,在其lib 子目錄下提供了Struts 框架所需 的JAR文件: l commons-beanutils.jar l commons-collections.jar l commons-digester.jar l commons-fileupload.jar l commons-logging.jar l commons-validator.jar l jakarta-oro.jar l struts.jar 在Struts軟件包的lib 子目錄下還提供了所有的Struts標(biāo)簽庫(kù)描述TLD 文件: l struts-bean.tld l struts-html.tld l struts-logic.tld l struts-nested.tld l struts-tiles.tld 圖2-4顯示了helloapp應(yīng)用的目錄結(jié)構(gòu),。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 36 圖2-4 helloapp應(yīng)用的目錄結(jié)構(gòu) helloapp 應(yīng)用的Java 源文件位于helloapp/src 目錄下,編譯這些Java 源文件時(shí),,應(yīng)該 把Servlet API的JAR文件以及Struts的struts.jar 文件加到classpath中,。如果在本地安裝了 Tomcat 服務(wù)器,假定Tomcat 的根目錄為<CATALINA_HOME>,在<CATALINA_ HOME>\common\lib 目錄下提供了servlet-api.jar 文件,。 在本書(shū)配套光盤的sourcecode/helloapp/version1/helloapp目錄下提供了該應(yīng)用的所有源 文件,,只要把整個(gè)helloapp子目錄拷貝到<CATALINA_HOME>/webapps下,就可以按開(kāi)放 式目錄結(jié)構(gòu)發(fā)布這個(gè)應(yīng)用,。 如果helloapp 應(yīng)用開(kāi)發(fā)完畢,,進(jìn)入產(chǎn)品發(fā)布階段,應(yīng)該將整個(gè)Web 應(yīng)用打包為WAR 文件,,再進(jìn)行發(fā)布,。在本例中,也可以按如下步驟在Tomcat服務(wù)器上發(fā)布helloapp應(yīng)用,。 (1)在DOS下轉(zhuǎn)到helloapp應(yīng)用的根目錄,。 (2)把整個(gè)Web應(yīng)用打包為helloapp.war 文件,命令如下: jar cvf helloapp.war *.* (3)把helloapp.war 文件拷貝到<CATALINA_HOME>/webapps目錄下,。 (4)啟動(dòng)Tomcat 服務(wù)器,。Tomcat 服務(wù)器啟動(dòng)時(shí),會(huì)把webapps 目錄下的所有WAR 文件自動(dòng)展開(kāi)為開(kāi)放式的目錄結(jié)構(gòu),。所以在服務(wù)器啟動(dòng)后,,會(huì)發(fā)現(xiàn)服務(wù)器把helloapp.war 展開(kāi)到<CATALINA_HOME> /webapps/helloapp目錄中。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 37 (5)通過(guò)瀏覽器訪問(wèn)http://localhost:8080/helloapp/hello.jsp,。 2.8.1 服務(wù)器端裝載hello.jsp的流程 在Tomcat 服務(wù)器上成功發(fā)布了helloapp 應(yīng)用后,,訪問(wèn)http://localhost:8080/helloapp/ hello.jsp會(huì)看到如圖2-5所示的網(wǎng)頁(yè)。服務(wù)器端裝載hello.jsp網(wǎng)頁(yè)的流程如下,。 (1)<bean:message>標(biāo)簽從Resource Bundle中讀取文本,把它輸出到網(wǎng)頁(yè)上,。 (2)<html:form>標(biāo)簽在request范圍中查找HelloForm Bean,。如果存在這樣的實(shí)例, 就把HelloForm對(duì)象中的userName屬性賦值給HTML表單的userName文本框,。由于此時(shí) 還不存在HelloForm對(duì)象,,所以忽略這項(xiàng)操作。 (3)把hello.jsp的視圖呈現(xiàn)給客戶,。 圖2-5 直接訪問(wèn)hello.jsp的輸出網(wǎng)頁(yè) 2.8.2 表單驗(yàn)證的流程 在hello.jsp 網(wǎng)頁(yè)上,,不輸入姓名,直接單擊【Submit】按鈕,,會(huì)看到如圖2-6 所示的 網(wǎng)頁(yè),。 圖2-6 表單驗(yàn)證失敗的hello.jsp網(wǎng)頁(yè) 當(dāng)客戶提交HelloForm表單時(shí),請(qǐng)求路徑為“/HelloWorld.do”: <html:form action="/HelloWorld.do" focus="userName" > 服務(wù)器端執(zhí)行表單驗(yàn)證流程如下,。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 38 (1)Servlet容器在web.xml文件中尋找<url-pattern>屬性為“*.do”的<servlet-mapping> 元素: <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> (2)Servlet 容器依據(jù)以上<servlet-mapping>元素的<servlet-name>屬性“action”,,在 web.xml文件中尋找匹配的<servlet>元素: <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet> (3)Servlet 容器把請(qǐng)求轉(zhuǎn)發(fā)給以上<servlet>元素指定的ActionServlet,ActionServlet 依據(jù)用戶請(qǐng)求路徑“/HelloWorld.do”,在Struts配置文件中檢索path屬性為“/HelloWorld” 的<action>元素: <action path = "/HelloWorld" type = "hello.HelloAction" name = "HelloForm" scope = "request" validate = "true" input = "/hello.jsp" > <forward name="SayHello" path="/hello.jsp" /> </action> 更確切地說(shuō),,ActionServlet 此時(shí)檢索的是ActionMapping 對(duì)象,,而不 是直接訪問(wèn)Struts配置文件中的<action>元素。因?yàn)樵贏ctionServlet初始化 的時(shí)候,,會(huì)加載Struts 配置文件,,把各種配置信息保存在相應(yīng)的配置類的 實(shí)例中,例如<action>元素的配置信息存放在ActionMapping對(duì)象中,。 (4)ActionServlet根據(jù)<action>元素的name屬性,,創(chuàng)建一個(gè)HelloForm對(duì)象,把客戶 提交的表單數(shù)據(jù)傳給HelloForm對(duì)象,,再把HelloForm對(duì)象保存在<action>元素的scope屬 性指定的request范圍內(nèi),。 (5)由于<action>元素的validate 屬性為true,ActionServlet 調(diào)用HelloForm 對(duì)象的 validate()方法執(zhí)行表單驗(yàn)證: public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 39 if ((userName == null) || (userName.length() < 1)) errors.add("username", new ActionMessage("hello.no.username.error")); return errors; } (6)HelloForm 對(duì)象的validate()方法返回一個(gè)ActionErrors 對(duì)象,,里面包含一個(gè) ActionMessage 對(duì)象,,這個(gè)ActionMessage 對(duì)象中封裝了錯(cuò)誤消息,消息key 為 “hello.no.username.error”,,在Resource Bundle中與值匹配的消息文本為: hello.no.username.error=Please enter a <i>UserName</i> to say hello to! (7)ActionServlet 把HelloForm 的validate()方法返回的ActionErrors 對(duì)象保存在 request范圍內(nèi),,然后根據(jù)<action>元素的input屬性,把客戶請(qǐng)求轉(zhuǎn)發(fā)給hello.jsp,。 (8)hello.jsp 的<html:errors>標(biāo)簽從request 范圍內(nèi)讀取ActionErrors 對(duì)象,,再?gòu)?br>ActionErrors對(duì)象中讀取ActionMessage對(duì)象,把它包含的錯(cuò)誤消息顯示在網(wǎng)頁(yè)上,。 2.8.3 邏輯驗(yàn)證失敗的流程 接下來(lái)在hello.jsp 的HTML 表單中輸入姓名“Monster”,,然后單擊【Submit】按鈕。 當(dāng)服務(wù)器端響應(yīng)客戶請(qǐng)求時(shí),,驗(yàn)證流程如下,。 (1)重復(fù)2.8.2 小節(jié)的流程(1)~(4)。 (2)ActionServlet 調(diào)用HelloForm 對(duì)象的validate()方法,,這次validate()方法返回的 ActionErrors對(duì)象中不包含任何ActionMessage對(duì)象,,表示表單驗(yàn)證成功。 (3)ActionServlet 查找HelloAction 實(shí)例是否存在,,如果不存在就創(chuàng)建一個(gè)實(shí)例,。然 后調(diào)用HelloAction的execute()方法。 (4)HelloAction 的execute()方法先進(jìn)行邏輯驗(yàn)證,,由于沒(méi)有通過(guò)邏輯驗(yàn)證,,就創(chuàng)建 一個(gè)ActionMessage 對(duì)象,這個(gè)ActionMessage 對(duì)象封裝了錯(cuò)誤消息,,消息key 為 “hello.dont.talk.to.monster”,,在Resource Bundle中與值匹配的消息文本為: hello.dont.talk.to.monster=We don‘t want to say hello to Monster!!! execute()方法把ActionMessage 對(duì)象保存在ActionMessages 對(duì)象中,,再把 ActionMessages 對(duì)象存放在request 范圍內(nèi)。最后返回一個(gè)ActionForward 對(duì)象,,該對(duì)象包 含的請(qǐng)求轉(zhuǎn)發(fā)路徑為<action>元素的input屬性指定的hello.jsp,。 以下是execute()方法中進(jìn)行邏輯驗(yàn)證的代碼: ActionMessages errors = new ActionMessages(); String userName = (String)((HelloForm) form).getUserName(); String badUserName = "Monster"; if (userName.equalsIgnoreCase(badUserName)) { PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 40 errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName )); saveErrors(request, errors); return (new ActionForward(mapping.getInput())); } (5)ActionServlet 依據(jù)HelloAction 返回的ActionForward 對(duì)象,再把請(qǐng)求轉(zhuǎn)發(fā)給 hello.jsp,。 (6)hello.jsp 的<html:errors>標(biāo)簽從request 范圍內(nèi)讀取ActionMessages 對(duì)象,,再?gòu)?br>ActionMessages 對(duì)象中讀取ActionMessage對(duì)象,把它包含的錯(cuò)誤消息顯示在網(wǎng)頁(yè)上,,如圖 2-7所示,。 圖2-7 邏輯驗(yàn)證失敗時(shí)的hello.jsp網(wǎng)頁(yè) 2.8.4 邏輯驗(yàn)證成功的流程 接下來(lái),在hello.jsp的HTML表單中輸入姓名“Weiqin”,,然后單擊【Submit】按鈕,。 當(dāng)服務(wù)器端響應(yīng)客戶請(qǐng)求時(shí),流程如下,。 (1)重復(fù)2.8.3 節(jié)的流程(1)~(3),。 (2)HelloAction 的execute()方法先執(zhí)行邏輯驗(yàn)證,這次通過(guò)了驗(yàn)證,,然后執(zhí)行相關(guān) 的業(yè)務(wù)邏輯,,最后調(diào)用ActionMapping.findForward()方法,參數(shù)為“SayHello”: // Forward control to the specified success URI return (mapping.findForward("SayHello")); (3)ActionMapping.findForward()方法從<action>元素中尋找name屬性為“SayHello” 的<forward>子元素,,然后返回與之對(duì)應(yīng)的ActionForward 對(duì)象,,它代表的請(qǐng)求轉(zhuǎn)發(fā)路徑為 “/hello.jsp”。 更確切地說(shuō),,ActionMapping 從本身包含的HashMap 中查找name 屬 性為“SayHello”的ActionForward對(duì)象,。在ActionServlet初始化時(shí)會(huì)加載 Struts配置文件,把<action>元素的配置信息存放在ActionMapping對(duì)象中,。 <action>元素中可以包含多個(gè)<forward>子元素,,每個(gè)<forward>子元素的配 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 第2 章 第一個(gè)Struts應(yīng)用:helloapp應(yīng)用 41 置信息存放在一個(gè)ActionForward對(duì)象中,這些ActionForward對(duì)象存放在 ActionMapping對(duì)象的HashMap中,。 (4)HelloAction 的execute()方法然后把ActionForward 對(duì)象返回給ActionServlet, ActionServlet再把客戶請(qǐng)求轉(zhuǎn)發(fā)給hello.jsp,。 (5)hello.jsp的<bean:message>標(biāo)簽從Resource Bundle中讀取文本,,把它們輸出到網(wǎng) 頁(yè)上,最后生成動(dòng)態(tài)網(wǎng)頁(yè),,如圖2-8所示,。 圖2-8 通過(guò)數(shù)據(jù)驗(yàn)證的hello.jsp網(wǎng)頁(yè) 2.9 小 結(jié) 本章通過(guò)簡(jiǎn)單但完整的helloapp應(yīng)用的例子,演示了如何把Struts框架運(yùn)用到Web 應(yīng) 用的開(kāi)發(fā)中,。通過(guò)這個(gè)例子,,讀者可以掌握以下內(nèi)容: l 分析應(yīng)用需求,,把應(yīng)用分解為模型、視圖和控制器來(lái)實(shí)現(xiàn)這些需求,。 l 利用Struts 的標(biāo)簽庫(kù)來(lái)創(chuàng)建視圖組件,。視圖組件中的文本內(nèi)容保存在專門的消息 資源文件中,在JSP文件中通過(guò)Struts的<bean:message>標(biāo)簽來(lái)訪問(wèn)它,,這樣可以 很方便地實(shí)現(xiàn)Struts應(yīng)用的國(guó)際化,,支持多國(guó)語(yǔ)言。 l Struts 框架采用ActionForm Bean 把視圖中的表單數(shù)據(jù)傳給控制器組件,。 ActionForm Bean 被存放在request 或session 范圍內(nèi),,它能夠被JSP 組件、Struts 標(biāo)簽以及Action類共享,。 l 數(shù)據(jù)驗(yàn)證分為兩種類型:HTML表單驗(yàn)證和業(yè)務(wù)邏輯驗(yàn)證,。表單驗(yàn)證由ActionForm Bean的validate()方法來(lái)實(shí)現(xiàn)。業(yè)務(wù)邏輯驗(yàn)證由Action類或模型組件來(lái)實(shí)現(xiàn),。 l ActionMessage 可以表示數(shù)據(jù)驗(yàn)證錯(cuò)誤,,它被保存在ActionMessages(或其子類 ActionErrors)集合對(duì)象中。ActionMessages 對(duì)象被保存在request 范圍內(nèi),,Struts 的視圖組件可以通過(guò)<html:errors>標(biāo)簽來(lái)訪問(wèn)它,。 l Action 類的execute()方法調(diào)用模型組件來(lái)完成業(yè)務(wù)邏輯,它還能決定把客戶請(qǐng)求 轉(zhuǎn)發(fā)給哪個(gè)視圖組件,。 l 模型組件具有封裝業(yè)務(wù)實(shí)現(xiàn)細(xì)節(jié)的功能,,開(kāi)發(fā)者可以方便地把模型組件移植到遠(yuǎn) PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. 精通Struts:基于MVC的Java Web設(shè)計(jì)與開(kāi)發(fā) 42 程應(yīng)用服務(wù)器上,這不會(huì)對(duì)MVC的其他模塊造成影響,。 l 通過(guò)調(diào)用HttpServletRequest或HttpSession的setAttribute()以及getAttribute()方法,, 可以保存或訪問(wèn)在request或session范圍內(nèi)的Java 對(duì)象,從而實(shí)現(xiàn)視圖組件和控 制器組件之間信息的交互與共享,。 l 利用struts-config.xml文件來(lái)配置Struts應(yīng)用,。 PDF 文件使用 "pdfFactory" 試用版本創(chuàng)建 www. |
|