(第2部分)
下面,我們就一步步按照上面所說的步驟來完成我們的應用程序:
第一步,我們的應用程序的Views部分包含兩個.jsp頁面:一個是登錄頁面logon.jsp,另一個是用戶登錄成功後的用戶功能頁main.jsp,暫時這個頁面只是個簡單的歡迎頁面。
其中,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文件,會發現它有這麼兩個鮮明的特點:一是文件頭部有諸如:
這樣的指令代碼,他們的作用就是指示頁面要用到struts的自定義標籤,標籤庫uri是一個邏輯引用,標籤庫的描述符(tld)的位置在web.xml文件中給出,見上篇文章的配置部分。struts的標籤庫主要由四組標籤組成,它們分別是:
logic標籤,作用是在jsp中進行流程控制
html標籤,作用是顯示錶單等組件
template標籤,作用是生成動態模板
關於每類標籤的具體作用及語法,因受篇幅限制,不在這裏詳細討論,大家可參考struts手冊之類的資料。只是心裏要明白所謂標籤其後面的東西就是一些類,這點與bean有些相似,它們在後端運行,生成標準的html標籤返回給瀏覽器。
要使用它們顯然要把它們的標籤庫描述文件引入到我們的系統中,這是些以.tld爲擴展名的文件,我們要把它們放在/webapps/mystruts/WEB-INF/目錄下。引入struts標籤後原來普通的html標籤如文本框的標籤變成了這樣的形式。
Jsp文件的第二個特點,就是頁面上根本沒有直接寫用於顯示的文字如:username,password等東西,而是用這種形式出現。這個特點爲國際化編程打下了堅實的基礎,關於國際化編程後面的文章還會專門討論。
這個簡單的應用所用到的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;
}
}
在你的應用程序的WEB-INF目錄下再建一個classes目錄,在新建的這個classes目錄下再建如下幾個目錄entity(用於存放ActionForm類)、action目錄(用於存放Action類)、bussness目錄(用於存放作爲Model的業務對象類)。Classes目錄下的子目錄就是所謂的包,以後,還會根據需要增加相應的包。現在,將UserInfoForm.java保存到entity目錄中。
把如下代碼添加到/webapps/mystruts/WEB-INF/struts-config.xml文件中
<form-beans>
<form-bean name="userInfoForm" type="entity.UserInfoForm" />
</form-beans>
特別要提醒一下的是:關於ActionForm的大小寫,一定要按照上面的寫,以免造成不必要的麻煩。
到此,我們完成了第一步工作。
第二步,我們建一個名爲ApplicationResource.properties的文件,並把它放在/webapps/mystruts/WEB-INF/classes目錄下。它在struts-config.xml的配置信息我們已在第一篇文章的末尾說了,就是:
目前我們在ApplicationResource.properties文件中加入的內容是:
#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:
到此,我們已完成了第二個步驟。
第三步,我們開始生成和配置Controller組件。
在前面我們已經提到,Struts應用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action類組成,其中,前者已由Struts準備好了,後者Struts只是爲我們提供了個骨架,我們要做的是爲實現應用程序的特定功能而擴展Action類,下面是實現我們登錄程序的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();
//聲明錯誤集對象
ActionErrors errors = new ActionErrors();
//校驗輸入
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);
}
//調用業務邏輯
if(errors.size()==0){
String validated = "";
try{
UserInfoBo userInfoBo=new UserInfoBo();
validated =userInfoBo.validatePwd(username,password);
if(validated.equals("match")){
//一切正常就保存用戶信息並轉向成功的頁面
HttpSession session = request.getSession();
session.setAttribute("userInfoForm", form);
return mapping.findForward("success");
}
}
catch(Throwable e){
//處理可能出現的錯誤
e.printStackTrace();
ActionError error=new ActionError(e.getMessage());
errors.add(ActionErrors.GLOBAL_ERROR,error);
}
}
//如出錯就轉向輸入頁面,並顯示相應的錯誤信息
saveErrors(request, errors);
return new ActionForward(mapping.getInput());
}
}
這個action類中有兩個錯誤消息鍵要加到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的關係,也就是配置所謂的ActionMapping。它們在struts-config.xml中的位置是排在… 標籤後,我們的登錄程序的配置清單如下:
<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>
第五步:生成應用程序所需要的model組件,該組件是完成應用程序業務邏輯的地方,現在我的登錄程序的業務邏輯很簡單,就是判斷用戶是不是lhb並且其口令是不是awave如果是就返回一個表示匹配的字符串"match",否則,就拋出出錯信息。其代碼清單如下:
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包中。
我們同樣要將其表示錯誤信息的鍵值設置在ApplicationResource.properties文件中,清單如下:
#Application Resource for the UserInfoBo.java
error.noMatch=<li><font color="red">no matched user</font></li>
到此爲止,我們已經完成了這個簡單登錄程序的所有組件。下面就可以享受我們的勞動成果了。
第六步、編譯運行應用程序。
常規的做法是用Ant來裝配和部署Struts應用程序,如果按這個套路,這篇文章就會顯得十分冗長乏味,同時也沒有太大的必要,因爲,用一個IDE一般可以很方便地生成一個應用。因此,我們採用簡便的方法,直接編譯我們的.java文件。不過這裏要注意一點的是:實踐證明,要使得編譯過程不出錯,還必須將struts.jar文件放一份拷貝到/common/lib目錄中,並在環境變量中設置CLASSPATH 其值是/common/lib/struts.jar;配置好後就可以分別編譯entity、bussness及action目錄下的.java文件了。編譯完成後:打開/conf目錄下的server.xml文件,在前加上如下語句爲我們的應用程序建一個虛擬目錄:
<Context path="/mystruts" docBase="mystruts" debug="0"
reloadable="true">
</Context>
啓動,tomcat。在瀏覽器中輸入:http://localhost:8080/mystruts/logon.jsp
如果前面的步驟沒有紕漏的話,一個如圖所示的登錄畫面就會出現在你的眼前。
如果,不輸入任何內容直接點擊Submit按鈕,就會返回到logon.jsp並顯示missing username和missing password錯誤信息;如果輸入其他內容,則會返回no matched user的錯誤;如果輸入的用戶名是lhb且口令是awave則會顯示錶示登錄成功的歡迎頁面。
上面雖然是一個功能很簡單的應用程序,但麻雀雖小,五臟俱全,基本涉及到了struts的主要組成部分。下面我們就來分析一下程序的特點和基本的工作原理。
首先,我們在瀏覽器中輸入.jsp文件時,後臺將struts的自定義標籤"翻譯"成普通的html標籤返回給瀏覽器,而一些提示信息如作爲輸入框label的username、password還有按鈕上提示信息還有錯誤信息等都來自MessageResources即ApplicationResource.properties文件中對應的鍵值。當我們點擊Submit按鈕時,從web.xml的配置可以看出,請求將被ActionServlet截獲。它通過表單中提供的action參數在struts-config.xml文件中查找對應的項目,如果有對應的ActionForm,它就用表單中數據填充ActionForm的對應屬性,本例中的ActionForm爲userInfoForm,相應的屬性是username和password,這就是所謂的實例化ActionForm。然後,將控制交給對應的Action,本例中是LogonAction,它做的主要工作是對ActionForm中取出的username和password做了一下校驗,這裏只是簡單檢驗它們是否爲空(這些簡單的格式化方面的校驗應該放在客戶端進行,而且struts也爲我們提供了一個很好的模式,後面如果有可能會詳細介紹)。如果不爲空則調用判斷用戶及口令是否正確的業務邏輯模塊UserInfoBo,同時,它會捕獲可能出現的錯誤,然後根據業務邏輯返回的結果將程序導向不同的頁面,本例中如果業務邏輯返回的結果是"match"則依據中的返回main.jsp頁面給瀏覽器同時在session對象中保存了用戶的登錄信息;否則,返回輸入頁面並顯示相應的出錯信息,完成了上篇文章所說的它的四個主要職責。
大家一定注意到了,在本例的業務邏輯模塊UserInfoBo中,將用戶與密碼是寫死在程序中的,在一個真實的應用程序中是不會這樣做的,那些需要永久保存的信息如,username及口令等都會保存在數據庫文件之類的永久介質中,下一篇文章我們將介紹在struts中如何訪問數據庫 。