helloapp应用作为Java Web应用,它的目录结构应该符合Sun公司制定的Java Web应用的规范,此外,由于helloapp应用使用了Struts框架,因此应该把Struts框架所需的JAR文件和标签库描述文件TLD文件包含进来。访问http://jakarta.apache.org/builds,可以下载最新的Struts软件包,把struts压缩文件解压后,在其lib子目录下提供了Struts框架所需的JAR文件:
·commons-beanutils.jar
·commons-collections.jar
·commons-digester.jar
·commons-fileupload.jar
·commons-logging.jar
·commons-validator.jar
·jakarta-oro.jar
·struts.jar
在Struts软件包的lib子目录下还提供了所有的Struts标签库描述TLD文件:
·struts-bean.tld
·struts-html.tld
·struts-logic.tld
·struts-nested.tld
·struts-tiles.tld
图2-4显示了helloapp应用的目录结构。
图2-4 helloapp应用的目录结构
helloapp应用的Java源文件位于helloapp/src目录下,编译这些Java源文件时,应该把Servlet API的JAR文件以及Struts的struts.jar文件加到classpath中。如果在本地安装了Tomcat服务器,假定Tomcat的根目录为<CATALINA_HOME>,在<CATALINA_HOME>\common\lib目录下提供了servlet-api.jar文件。在本书配套光盘的sourcecode/helloapp/version1/helloapp目录下提供了该应用的所有源文件,只要把整个helloapp子目录拷贝到<CATALINA_HOME>/webapps下,就可以按开放式目录结构发布这个应用。
如果helloapp应用开发完毕,进入产品发布阶段,应该将整个Web应用打包为WAR文件,再进行发布。在本例中,也可以按如下步骤在Tomcat服务器上发布helloapp应用。
(1)在DOS下转到helloapp应用的根目录。
(2)把整个Web应用打包为helloapp.war文件,命令如下:jar cvf helloapp.war *.*
(3)把helloapp.war文件拷贝到<CATALINA_HOME>/webapps目录下。
(4)启动Tomcat服务器。Tomcat服务器启动时,会把webapps目录下的所有WAR文件自动展开为开放式的目录结构。所以服务器启动后,会发现服务器把helloapp.war展开到<CATALINA_HOME> /webapps/helloapp目录中。
(5)通过浏览器访问http://localhost:8080/helloapp/hello.jsp。
服务器端装载hello.jsp的流程
在Tomcat服务器上成功发布了helloapp应用后,访问http://localhost:8080/helloapp/hello.jsp,会看到如图2-5所示的网页。服务器端装载hello.jsp网页的流程如下。
(1)<bean:message>标签从Resource Bundle中读取文本,把它输出到网页上。
(2)<html:form>标签在request范围中查找HelloForm Bean。如果存在这样的实例,就把HelloForm对象中的userName属性赋值给HTML表单的userName文本框。由于此时还不存在HelloForm对象,所以忽略这项操作。
(3)把hello.jsp的视图呈现给客户。
图2-5 直接访问hello.jsp的输出网页
||||||表单验证的流程
在hello.jsp网页上,不输入姓名,直接单击【Submit】按钮,会看到如图2-6所示的网页。
图2-6 表单验证失败的hello.jsp网页
当客户提交HelloForm表单时,请求路径为"/HelloWorld.do":
<html:form action="/HelloWorld.do" focus="userName" >
服务器端执行表单验证流程如下。
(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容器依据以上<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容器把请求转发给以上<servlet>元素指定的ActionServlet,ActionServlet依据用户请求路径"/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>
更确切的说,ActionServlet此时检索的是ActionMapping对象,而不是直接访问Struts配置文件中的<action>元素。因为在ActionServlet初始化的时候,会加载Struts配置文件,把各种配置信息保存在相应的配置类的实例中,例如<action>元素的配置信息存放在ActionMapping对象中。
(4)ActionServlet根据<action>元素的name属性,创建一个HelloForm对象,把客户提交的表单数据传给HelloForm对象,再把HelloForm对象保存在<action>元素的scope属性指定的request范围内。
(5)由于<action>元素的validate属性为true,ActionServlet调用HelloForm对象的validate()方法执行表单验证:
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;
}
(6)HelloForm对象的validate()方法返回一个ActionErrors对象,里面包含一个ActionMessage对象,这个ActionMessage对象中封装了错误消息,消息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对象包存在request范围内,然后根据<action>元素的input属性,把客户请求转发给hello.jsp。
(8)hello.jsp的<html:errors>标签从request范围内读取ActionErrors对象,再从ActionErrors对象中读取ActionMessage对象,把它包含的错误消息显示在网页上。
逻辑验证失败的流程
接下来在hello.jsp的HTML表单中输入姓名"Monster",然后单击【Submit】按钮。当服务器端响应客户请求时,验证流程如下。
(1)重复表单验证的步骤1至4。
(2)ActionServlet调用HelloForm对象的validate()方法,这次validate()方法返回的ActionErrors对象中不包含任何ActionMessage对象,表示表单验证成功。
(3)ActionServlet查找HelloAction实例是否存在,如果不存在就创建一个实例。然后调用HelloAction的execute()方法。
(4)HelloAction的execute()方法先进行逻辑验证,由于没有通过逻辑验证,就创建一个ActionMessage对象,这个ActionMessage对象封装了错误消息,消息key为"hello.dont.talk.to.monster",在Resource Bundle中与值匹配的消息文本为:hello.dont.talk.to.monster=We don't want to say hello to Monster!!!
execute()方法把ActionMessage对象保存在ActionMessages对象中,再把ActionMessages对象存放在request范围内。最后返回一个ActionForward对象,该对象包含的请求转发路径为<action>元素的input属性指定的hello.jsp。
以下是execute()方法中进行逻辑验证的代码:
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()));
}
(5)ActionServlet依据HelloAction返回的ActionForward对象,再把请求转发给hello.jsp。
(6)hello.jsp的<html:errors>标签从request范围内读取ActionMessages对象,再从ActionMessages对象中读取ActionMessage对象,把它包含的错误消息显示在网页上。如图2-7所示。
图2-7 逻辑验证失败时的hello.jsp网页
逻辑验证成功的流程
接下来,在hello.jsp的HTML表单中输入姓名"Weiqin",然后单击【Submit】按钮。当服务器端响应客户请求时,流程如下。
(1)重复逻辑验证失败的步骤1至3。
(2)HelloAction的execute()方法先执行逻辑验证,这次通过了验证,然后执行相关的业务逻辑,最后调用ActionMapping.findForward()方法,参数为"SayHello":
// Forward control to the specified success URI
return (mapping.findForward("SayHello"));
(3)ActionMapping.findForward()方法从<action>元素中寻找name属性为"SayHello"的<forward>子元素,然后返回与之对应的ActionForward对象,它代表的请求转发路径为"/hello.jsp"。
更确切的说,ActionMapping从本身包含的HashMap中查找name属性为"SayHello"的ActionForward对象。在ActionServlet初始化时会加载Struts配置文件,把<action>元素的配置信息存放在ActionMapping对象中,<action>元素中可以包含多个<forward>子元素,每个<forward>子元素的配置信息存放在一个ActionForward对象中,这些ActionForward对象存放在ActionMapping对象的HashMap中。
(4)HelloAction的execute()方法然后把ActionForward对象返回给ActionServlet,ActionServlet再把客户请求转发给hello.jsp。
(5)hello.jsp的<bean:message>标签从Resource Bundle中读取文本,把它们输出到网页上,最后生成动态网页,如图2-8所示。
图2-8 通过数据验证的hello.jsp网页