Struts原理與實(shí)踐(2)- -
下面,我們就從一個(gè)最簡(jiǎn)單的登錄例子入手,以對(duì)Struts的主要部分有一些直觀而清晰的認(rèn)識(shí)。這個(gè)例子功能非常簡(jiǎn)單,假設(shè)有一個(gè)名為lhb的用戶,其密碼是awave,程序要完成的任務(wù)是,呈現(xiàn)一個(gè)登錄界面給用戶,如果用戶輸入的名稱和密碼都正確返回一個(gè)歡迎頁(yè)面給用戶,否則,就返回登錄頁(yè)面要求用戶重新登錄并顯示相應(yīng)的出錯(cuò)信息。這個(gè)例子在我們講述Struts的基礎(chǔ)部分時(shí)會(huì)反復(fù)用到。之所以選用這個(gè)簡(jiǎn)單的程序作為例子是因?yàn)椴幌胱屵^(guò)于復(fù)雜的業(yè)務(wù)邏輯來(lái)沖淡我們的主題。
因?yàn)镾truts是建立在MVC設(shè)計(jì)模式上的框架,你可以遵從標(biāo)準(zhǔn)的開(kāi)發(fā)步驟來(lái)開(kāi)發(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)用程序.
下面,我們就一步步按照上面所說(shuō)的步驟來(lái)完成我們的應(yīng)用程序:
第一步,我們的應(yīng)用程序的Views部分包含兩個(gè).jsp頁(yè)面:一個(gè)是登錄頁(yè)面logon.jsp,另一個(gè)是用戶登錄成功后的用戶功能頁(yè)main.jsp,暫時(shí)這個(gè)頁(yè)面只是個(gè)簡(jiǎn)單的歡迎頁(yè)面。
其中,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):一是文件頭部有諸如:
這樣的指令代碼,他們的作用就是指示頁(yè)面要用到struts的自定義標(biāo)簽,標(biāo)簽庫(kù)uri是一個(gè)邏輯引用,標(biāo)簽庫(kù)的描述符(tld)的位置在web.xml文件中給出,見(jiàn)上篇文章的配置部分。struts的標(biāo)簽庫(kù)主要由四組標(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)簽的具體作用及語(yǔ)法,因受篇幅限制,不在這里詳細(xì)討論,大家可參考struts手冊(cè)之類的資料。只是心里要明白所謂標(biāo)簽其后面的東西就是一些類,這點(diǎn)與bean有些相似,它們?cè)诤蠖诉\(yùn)行,生成標(biāo)準(zhǔn)的html標(biāo)簽返回給瀏覽器。
要使用它們顯然要把它們的標(biāo)簽庫(kù)描述文件引入到我們的系統(tǒng)中,這是些以.tld為擴(kuò)展名的文件,我們要把它們放在 /webapps/mystruts/WEB-INF/目錄下。引入struts標(biāo)簽后原來(lái)普通的html標(biāo)簽如文本框的標(biāo)簽變成了這樣的形式 。
Jsp文件的第二個(gè)特點(diǎn),就是頁(yè)面上根本沒(méi)有直接寫(xiě)用于顯示的文字如:username,password等東西,而是用 這種形式出現(xiàn)。這個(gè)特點(diǎn)為國(guó)際化編程打下了堅(jiān)實(shí)的基礎(chǔ),關(guān)于國(guó)際化編程后面的文章還會(huì)專門(mén)討論。
這個(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的大小寫(xiě),一定要按照上面的寫(xiě),以免造成不必要的麻煩。
到此,我們完成了第一步工作。
第二步,我們建一個(gè)名為ApplicationResource.properties的文件,并把它放在 /webapps/mystruts/WEB-INF/classes目錄下。它在struts-config.xml的配置信息我們已在第一篇文章的末尾說(shuō)了,就是:
目前我們?cè)贏pplicationResource.properties文件中加入的內(nèi)容是:
#Application Resource for the logon.jsp
logon.jsp.title=The logon page
logon.jsp.page.heading=Welcome World!
logon.jsp.prompt.username=Username:
logon.jsp.prompt.password=Password:
logon.jsp.prompt.submit=Submit
logon.jsp.prompt.reset=Reset
#Application Resource for the main.jsp
main.jsp.title=The main page
main.jsp.welcome=Welcome:
|
到此,我們已完成了第二個(gè)步驟。
第三步,我們開(kāi)始生成和配置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)向成功的頁(yè)面
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è)面,并顯示相應(yīng)的錯(cuò)誤信息
saveErrors(request, errors);
return new ActionForward(mapping.getInput());
}
}
|
這個(gè)action類中有兩個(gè)錯(cuò)誤消息鍵要加到ApplicationResource.properties文件中,清單如下:
#Application Resource for the LogonAction.java
error.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.java
error.noMatch=<li><font color="red">no matched user</font></li>
|
到此為止,我們已經(jīng)完成了這個(gè)簡(jiǎn)單登錄程序的所有組件。下面就可以享受我們的勞動(dòng)成果了。
第六步、編譯運(yùn)行應(yīng)用程序。
常規(guī)的做法是用Ant來(lái)裝配和部署Struts應(yīng)用程序,如果按這個(gè)套路,這篇文章就會(huì)顯得十分冗長(zhǎng)乏味,同時(shí)也沒(méi)有太大的必要,因?yàn)椋靡粋€(gè)IDE一般可以很方便地生成一個(gè)應(yīng)用。因此,我們采用簡(jiǎn)便的方法,直接編譯我們的.java文件。不過(guò)這里要注意一點(diǎn)的是:實(shí)踐證明,要使得編譯過(guò)程不出錯(cuò),還必須將struts.jar文件放一份拷貝到 /common/lib目錄中,并在環(huán)境變量中設(shè)置CLASSPATH 其值是 /common/lib/struts.jar;配置好后就可以分別編譯entity、bussness及action目錄下的.java文件了。編譯完成后:打開(kāi) /conf目錄下的server.xml文件,在 前加上如下語(yǔ)句為我們的應(yīng)用程序建一個(gè)虛擬目錄:
<Context path="/mystruts" docBase="mystruts" debug="0"
reloadable="true">
</Context>
|
啟動(dòng),tomcat。在瀏覽器中輸入:http://localhost:8080/mystruts/logon.jsp 如果前面的步驟沒(méi)有紕漏的話,一個(gè)如圖所示的登錄畫(huà)面就會(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ì)顯示表示登錄成功的歡迎頁(yè)面。
上面雖然是一個(gè)功能很簡(jiǎn)單的應(yīng)用程序,但麻雀雖小,五臟俱全,基本涉及到了struts的主要組成部分。下面我們就來(lái)分析一下程序的特點(diǎn)和基本的工作原理。
首先,我們?cè)跒g覽器中輸入.jsp文件時(shí),后臺(tái)將struts的自定義標(biāo)簽"翻譯"成普通的html標(biāo)簽返回給瀏覽器,而一些提示信息如作為輸入框label的username、password還有按鈕上提示信息還有錯(cuò)誤信息等都來(lái)自MessageResources即ApplicationResource.properties文件中對(duì)應(yīng)的鍵值。當(dāng)我們點(diǎn)擊Submit按鈕時(shí),從web.xml的配置可以看出,請(qǐng)求將被ActionServlet截獲。它通過(guò)表單中提供的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ū)虿煌捻?yè)面,本例中如果業(yè)務(wù)邏輯返回的結(jié)果是"match"則依據(jù) 中的 返回main.jsp頁(yè)面給瀏覽器同時(shí)在session對(duì)象中保存了用戶的登錄信息;否則,返回輸入頁(yè)面并顯示相應(yīng)的出錯(cuò)信息,完成了上篇文章所說(shuō)的它的四個(gè)主要職責(zé)。
大家一定注意到了,在本例的業(yè)務(wù)邏輯模塊UserInfoBo中,將用戶與密碼是寫(xiě)死在程序中的,在一個(gè)真實(shí)的應(yīng)用程序中是不會(huì)這樣做的,那些需要永久保存的信息如,username及口令等都會(huì)保存在數(shù)據(jù)庫(kù)文件之類的永久介質(zhì)中,下一篇文章我們將介紹在struts中如何訪問(wèn)數(shù)據(jù)庫(kù)。
|