Struts原理與實(shí)踐(2)- -
下面,我們就從一個(gè)最簡(jiǎn)單的登錄例子入手,以對(duì)Struts的主要部分有一些直觀而清晰的認(rèn)識(shí)。這個(gè)例子功能非常簡(jiǎn)單,假設(shè)有一個(gè)名為lhb的用戶,其密碼是awave,程序要完成的任務(wù)是,呈現(xiàn)一個(gè)登錄界面給用戶,如果用戶輸入的名稱和密碼都正確返回一個(gè)歡迎頁面給用戶,否則,就返回登錄頁面要求用戶重新登錄并顯示相應(yīng)的出錯(cuò)信息。這個(gè)例子在我們講述Struts的基礎(chǔ)部分時(shí)會(huì)反復(fù)用到。之所以選用這個(gè)簡(jiǎn)單的程序作為例子是因?yàn)椴幌胱屵^于復(fù)雜的業(yè)務(wù)邏輯來沖淡我們的主題。
因?yàn)镾truts是建立在MVC設(shè)計(jì)模式上的框架,你可以遵從標(biāo)準(zhǔn)的開發(fā)步驟來開發(fā)你的Struts Web應(yīng)用程序,這些步驟大致可以描述如下:
1定義并生成所有代表應(yīng)用程序的用戶接口的Views,同時(shí)生成這些Views所用到的所有ActionForms并將它們添加到struts-config.xml文件中。
2在ApplicationResource.properties文件中添加必要的MessageResources項(xiàng)目
3生成應(yīng)用程序的控制器。
4在struts-config.xml文件中定義Views與 Controller的關(guān)系。
5生成應(yīng)用程序所需要的model組件
6編譯、運(yùn)行你的應(yīng)用程序.
下面,我們就一步步按照上面所說的步驟來完成我們的應(yīng)用程序:
第一步,我們的應(yīng)用程序的Views部分包含兩個(gè).jsp頁面:一個(gè)是登錄頁面logon.jsp,另一個(gè)是用戶登錄成功后的用戶功能頁main.jsp,暫時(shí)這個(gè)頁面只是個(gè)簡(jiǎn)單的歡迎頁面。
其中,logon.jsp的代碼清單如下:
<%@ page contentType="text/html; charset=UTF-8" %><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %><%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %><HTML><HEAD><TITLE><bean:message key="logon.jsp.title"/></TITLE><html:base/></HEAD><BODY><h3><bean:message key="logon.jsp.page.heading"/></h3><html:errors/><html:form action="/logonAction.do" focus="username"><TABLE border="0" width="100%"><TR><TH align="right"><bean:message key="logon.jsp.prompt.username"/></TH><TD align="left"><html:text property="username"/></TD></TR><TR><TH align="right"><bean:message key="logon.jsp.prompt.password"/></TH><TD align="left"><html:password property="password"/></TD></TR><TR><TD align="right"> <html:submit><bean:message key="logon.jsp.prompt.submit"/></html:submit></TD><TD align="left"> <html:reset><bean:message key="logon.jsp.prompt.reset"/></html:reset></TD></TR></TABLE></html:form></BODY></HTML>
main.jsp的代碼清單如下:
<%@ page contentType="text/html; charset=UTF-8" %><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %><%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %><HTML><HEAD><TITLE><bean:message key="main.jsp.title"/></TITLE><html:base/></HEAD><BODY><logic:present name="userInfoForm"><H3> <bean:message key="main.jsp.welcome"/> <bean:write name="userInfoForm" property="username"/>!</H3></logic:present></BODY></HTML>
首先,我們看一下logon.jsp文件,會(huì)發(fā)現(xiàn)它有這么兩個(gè)鮮明的特點(diǎn):一是文件頭部有諸如:
這樣的指令代碼,他們的作用就是指示頁面要用到struts的自定義標(biāo)簽,標(biāo)簽庫uri是一個(gè)邏輯引用,標(biāo)簽庫的描述符(tld)的位置在web.xml文件中給出,見上篇文章的配置部分。struts的標(biāo)簽庫主要由四組標(biāo)簽組成,它們分別是:
bean標(biāo)簽,作用是在jsp中操縱bean
logic標(biāo)簽,作用是在jsp中進(jìn)行流程控制
html標(biāo)簽,作用是顯示表單等組件
template標(biāo)簽,作用是生成動(dòng)態(tài)模板
關(guān)于每類標(biāo)簽的具體作用及語法,因受篇幅限制,不在這里詳細(xì)討論,大家可參考struts手冊(cè)之類的資料。只是心里要明白所謂標(biāo)簽其后面的東西就是一些類,這點(diǎn)與bean有些相似,它們?cè)诤蠖诉\(yùn)行,生成標(biāo)準(zhǔn)的html標(biāo)簽返回給瀏覽器。
要使用它們顯然要把它們的標(biāo)簽庫描述文件引入到我們的系統(tǒng)中,這是些以.tld為擴(kuò)展名的文件,我們要把它們放在 /webapps/mystruts/WEB-INF/目錄下。引入struts標(biāo)簽后原來普通的html標(biāo)簽如文本框的標(biāo)簽變成了這樣的形式 。
Jsp文件的第二個(gè)特點(diǎn),就是頁面上根本沒有直接寫用于顯示的文字如:username,password等東西,而是用 這種形式出現(xiàn)。這個(gè)特點(diǎn)為國際化編程打下了堅(jiān)實(shí)的基礎(chǔ),關(guān)于國際化編程后面的文章還會(huì)專門討論。
這個(gè)簡(jiǎn)單的應(yīng)用所用到的ActionForm為UserInfoForm,代碼清單如下:
package entity;import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionMapping;import javax.servlet.http.HttpServletRequest;public class UserInfoForm extends ActionForm{ private String username; private String password; public String getUsername() { return (this.username); } public void setUsername(String username) { this.username = username; } public String getPassword() { return (this.password); } public void setPassword(String password) { this.password = password; }}
在你的應(yīng)用程序的WEB-INF目錄下再建一個(gè)classes目錄,在新建的這個(gè)classes目錄下再建如下幾個(gè)目錄entity(用于存放ActionForm類)、action目錄(用于存放Action類)、bussness目錄(用于存放作為Model的業(yè)務(wù)對(duì)象類)。Classes目錄下的子目錄就是所謂的包,以后,還會(huì)根據(jù)需要增加相應(yīng)的包。
現(xiàn)在,將UserInfoForm.java保存到entity目錄中。
把如下代碼添加到 /webapps/mystruts/WEB-INF/struts-config.xml文件中
<form-beans> <form-bean name="userInfoForm" type="entity.UserInfoForm" /> </form-beans>
特別要提醒一下的是:關(guān)于ActionForm的大小寫,一定要按照上面的寫,以免造成不必要的麻煩。
到此,我們完成了第一步工作。
第二步,我們建一個(gè)名為ApplicationResource.properties的文件,并把它放在 /webapps/mystruts/WEB-INF/classes目錄下。它在struts-config.xml的配置信息我們已在第一篇文章的末尾說了,就是:
目前我們?cè)贏pplicationResource.properties文件中加入的內(nèi)容是:
#Application Resource for the logon.jsplogon.jsp.title=The logon pagelogon.jsp.page.heading=Welcome World!logon.jsp.prompt.username=Username:logon.jsp.prompt.password=Password:logon.jsp.prompt.submit=Submitlogon.jsp.prompt.reset=Reset#Application Resource for the main.jspmain.jsp.title=The main pagemain.jsp.welcome=Welcome:
到此,我們已完成了第二個(gè)步驟。
第三步,我們開始生成和配置Controller組件。
在前面我們已經(jīng)提到,Struts應(yīng)用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action類組成,其中,前者已由Struts準(zhǔn)備好了,后者Struts只是為我們提供了個(gè)骨架,我們要做的是為實(shí)現(xiàn)應(yīng)用程序的特定功能而擴(kuò)展Action類,下面是實(shí)現(xiàn)我們登錄程序的Action類的代碼清單:
package action;import java.io.IOException;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.ActionError;import org.apache.struts.action.ActionErrors;import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionMapping;import org.apache.struts.action.ActionServlet;import bussness.UserInfoBo;import entity.UserInfoForm;public final class LogonAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { UserInfoForm userInfoForm = (UserInfoForm) form; //從web層獲得用戶名和口令 String username = userInfoForm.getUsername().trim(); String password = userInfoForm.getPassword().trim(); //聲明錯(cuò)誤集對(duì)象 ActionErrors errors = new ActionErrors(); //校驗(yàn)輸入 if(username.equals("")){ ActionError error=new ActionError("error.missing.username"); errors.add(ActionErrors.GLOBAL_ERROR,error); } if(password.equals("")){ ActionError error=new ActionError("error.missing.password"); errors.add(ActionErrors.GLOBAL_ERROR,error); } //調(diào)用業(yè)務(wù)邏輯 if(errors.size()==0){ String validated = ""; try{ UserInfoBo userInfoBo=new UserInfoBo(); validated =userInfoBo.validatePwd(username,password); if(validated.equals("match")){ //一切正常就保存用戶信息并轉(zhuǎn)向成功的頁面 HttpSession session = request.getSession(); session.setAttribute("userInfoForm", form); return mapping.findForward("success"); } } catch(Throwable e){ //處理可能出現(xiàn)的錯(cuò)誤 e.printStackTrace(); ActionError error=new ActionError(e.getMessage()); errors.add(ActionErrors.GLOBAL_ERROR,error); } } //如出錯(cuò)就轉(zhuǎn)向輸入頁面,并顯示相應(yīng)的錯(cuò)誤信息 saveErrors(request, errors); return new ActionForward(mapping.getInput()); } }
這個(gè)action類中有兩個(gè)錯(cuò)誤消息鍵要加到ApplicationResource.properties文件中,清單如下:
#Application Resource for the LogonAction.javaerror.missing.username=<li><font color="red">missing username</font></li>error.missing.password=<li><font color="red">missing password</font></li>>
第四步:在struts-config.xml文件中定義Views與 Controller的關(guān)系,也就是配置所謂的ActionMapping。它們?cè)趕truts-config.xml中的位置是排在 … 標(biāo)簽后,我們的登錄程序的配置清單如下:
<action-mappings> <action input="/logon.jsp" name="userInfoForm" path="/logonAction" scope="session" type="action.LogonAction" validate="false"> <forward name="success" path="/main.jsp" /> </action> </action-mappings>
第五步:生成應(yīng)用程序所需要的model組件,該組件是完成應(yīng)用程序業(yè)務(wù)邏輯的地方,現(xiàn)在我的登錄程序的業(yè)務(wù)邏輯很簡(jiǎn)單,就是判斷用戶是不是lhb并且其口令是不是awave如果是就返回一個(gè)表示匹配的字符串"match",否則,就拋出出錯(cuò)信息。其代碼清單如下:
package bussness;import entity.UserInfoForm;public class UserInfoBo { public UserInfoBo(){ } public String validatePwd(String username,String password){ String validateResult=""; if(username.equals("lhb")&&password.equals("awave")){ validateResult="match"; } else{ throw new RuntimeException("error.noMatch"); } return validateResult; }}
將其放在bussness包中。
我們同樣要將其表示錯(cuò)誤信息的鍵值設(shè)置在ApplicationResource.properties文件中,清單如下:
#Application Resource for the UserInfoBo.javaerror.noMatch=<li><font color="red">no matched user</font></li>
到此為止,我們已經(jīng)完成了這個(gè)簡(jiǎn)單登錄程序的所有組件。下面就可以享受我們的勞動(dòng)成果了。
第六步、編譯運(yùn)行應(yīng)用程序。
常規(guī)的做法是用Ant來裝配和部署Struts應(yīng)用程序,如果按這個(gè)套路,這篇文章就會(huì)顯得十分冗長(zhǎng)乏味,同時(shí)也沒有太大的必要,因?yàn)椋靡粋€(gè)IDE一般可以很方便地生成一個(gè)應(yīng)用。因此,我們采用簡(jiǎn)便的方法,直接編譯我們的.java文件。不過這里要注意一點(diǎn)的是:實(shí)踐證明,要使得編譯過程不出錯(cuò),還必須將struts.jar文件放一份拷貝到 /common/lib目錄中,并在環(huán)境變量中設(shè)置CLASSPATH 其值是 /common/lib/struts.jar;配置好后就可以分別編譯entity、bussness及action目錄下的.java文件了。編譯完成后:打開 /conf目錄下的server.xml文件,在 前加上如下語句為我們的應(yīng)用程序建一個(gè)虛擬目錄:
<Context path="/mystruts" docBase="mystruts" debug="0" reloadable="true"> </Context>
啟動(dòng),tomcat。在瀏覽器中輸入:http://localhost:8080/mystruts/logon.jsp
如果前面的步驟沒有紕漏的話,一個(gè)如圖所示的登錄畫面就會(huì)出現(xiàn)在你的眼前。
如果,不輸入任何內(nèi)容直接點(diǎn)擊Submit按鈕,就會(huì)返回到logon.jsp并顯示missing username和missing password錯(cuò)誤信息;如果輸入其他內(nèi)容,則會(huì)返回no matched user的錯(cuò)誤;如果輸入的用戶名是lhb且口令是awave則會(huì)顯示表示登錄成功的歡迎頁面。
上面雖然是一個(gè)功能很簡(jiǎn)單的應(yīng)用程序,但麻雀雖小,五臟俱全,基本涉及到了struts的主要組成部分。下面我們就來分析一下程序的特點(diǎn)和基本的工作原理。
首先,我們?cè)跒g覽器中輸入.jsp文件時(shí),后臺(tái)將struts的自定義標(biāo)簽"翻譯"成普通的html標(biāo)簽返回給瀏覽器,而一些提示信息如作為輸入框label的username、password還有按鈕上提示信息還有錯(cuò)誤信息等都來自MessageResources即ApplicationResource.properties文件中對(duì)應(yīng)的鍵值。當(dāng)我們點(diǎn)擊Submit按鈕時(shí),從web.xml的配置可以看出,請(qǐng)求將被ActionServlet截獲。它通過表單中提供的action參數(shù)在struts-config.xml文件中查找對(duì)應(yīng)的 項(xiàng)目,如果有對(duì)應(yīng)的ActionForm,它就用表單中數(shù)據(jù)填充ActionForm的對(duì)應(yīng)屬性,本例中的ActionForm為userInfoForm,相應(yīng)的屬性是username和password,這就是所謂的實(shí)例化ActionForm。然后,將控制交給對(duì)應(yīng)的Action,本例中是LogonAction,它做的主要工作是對(duì)ActionForm中取出的username和password做了一下校驗(yàn),這里只是簡(jiǎn)單檢驗(yàn)它們是否為空(這些簡(jiǎn)單的格式化方面的校驗(yàn)應(yīng)該放在客戶端進(jìn)行,而且struts也為我們提供了一個(gè)很好的模式,后面如果有可能會(huì)詳細(xì)介紹)。如果不為空則調(diào)用判斷用戶及口令是否正確的業(yè)務(wù)邏輯模塊UserInfoBo,同時(shí),它會(huì)捕獲可能出現(xiàn)的錯(cuò)誤,然后根據(jù)業(yè)務(wù)邏輯返回的結(jié)果將程序?qū)虿煌捻撁妫纠腥绻麡I(yè)務(wù)邏輯返回的結(jié)果是"match"則依據(jù) 中的 返回main.jsp頁面給瀏覽器同時(shí)在session對(duì)象中保存了用戶的登錄信息;否則,返回輸入頁面并顯示相應(yīng)的出錯(cuò)信息,完成了上篇文章所說的它的四個(gè)主要職責(zé)。
大家一定注意到了,在本例的業(yè)務(wù)邏輯模塊UserInfoBo中,將用戶與密碼是寫死在程序中的,在一個(gè)真實(shí)的應(yīng)用程序中是不會(huì)這樣做的,那些需要永久保存的信息如,username及口令等都會(huì)保存在數(shù)據(jù)庫文件之類的永久介質(zhì)中,下一篇文章我們將介紹在struts中如何訪問數(shù)據(jù)庫。