</TD>
</TR>
</TABLE>
</html:form>
</BODY>
</HTML>
main.jsp的代码清单如下:
quote:
<%@ 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的标签库主要由四组标签组成,它们分别是:
bean标签,作用是在jsp中操纵bean
logic标签,作用是在jsp中进行流程控制
html标签,作用是显示表单等组件
template标签,作用是生成动态模板
关于每类标签的具体作用及语法,因受篇幅限制,不在这里详细讨论,大家可参考struts手册之类的资料。只是心里要明白所谓标签其后面的东西就是一些类,这点与bean有些相似,它们在后端运行,生成标准的html标签返回给浏览器。
要使用它们显然要把它们的标签库描述文件引入到我们的系统中,这是些以.tld为扩展名的文件,我们要把它们放在/webapps/mystruts/WEB-INF/目录下。引入struts标签后原来普通的html标签如文本框的标签变成了这样的形式。
Jsp文件的第二个特点,就是页面上根本没有直接写用于显示的文字如:username,password等东西,而是用这种形式出现。这个特点为国际化编程打下了坚实的基础,关于国际化编程后面的文章还会专门讨论。
这个简单的应用所用到的ActionForm为UserInfoForm,代码清单如下:
quote:
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文件中
quote:
<form-beans>
<form-bean name=/"userInfoForm/" type=/"entity.UserInfoForm/" />
</form-beans>
特别要提醒一下的是:关于ActionForm的大小写,一定要按照上面的写,以免造成不必要的麻烦。
到此,我们完成了第一步工作。
第二步,我们建一个名为ApplicationResource.properties的文件,并把它放在/webapps/mystruts/WEB-INF/classes目录下。它在struts-config.xml的配置信息我们已在第一篇文章的末尾说了,就是:
目前我们在ApplicationResource.properties文件中加入的内容是:
quote:
#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类的代码清单:
quote:
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文件中,清单如下:
quote:
#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中的位置是排在… 标签后,我们的登录程序的配置清单如下:
quote:
<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/",否则,就抛出出错信息。其代码清单如下:
quote:
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文件中,清单如下:
quote:
#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文件,在前加上如下语句为我们的应用程序建一个虚拟目录:
quote:
<Context path=/"/mystruts/" docBase=/"mystruts/" debug=/"0/"
reloadable=/"true/">
</Context>
启动,tomcat。在浏览器中输入:http://localhost:8080/mystruts/logon.jsp
如果前面的步骤没有纰漏的话,一个如图所示的登录画面就会出现在你的眼前
![open in new window](http://tech.ccidnet.com/pub/attachment/2004/8/320304.gif)
如果,不输入任何内容直接点击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中如何访问数据库。
本文作者:罗会波 当阳市国税局信息中心 可通过lhbf@sina.com与他联系