Struts2框架学习第三章——Struts2基础
本章要点
— Struts 1框架的基本知识
— 使用Struts 1框架开发Web应用
— WebWork框架的基本知识
— 使用WebWork框架开发Web应用
— 在Eclipse中整合Tomcat
— 使用Eclipse开发Web应用
— 为Web应用增加Struts 2支持
— Struts 2框架的MVC组件
— Struts 2框架的流程
— 通过web.xml文件加载Struts 2框架
— 通过struts.properties文件配置Struts 2属性
— struts.xml文件的结构
— 前面一章已经大致介绍了Struts 2应用的基本情况,通过前一章的学习,读者应该对Struts 2框架有了一个大致的掌握。但对于各知识点的细节,还需要进一步学习,本章将详细介绍Struts 2框架的基础部分。
因为Struts 2是在WebWork和Struts 1的基础上发展起来的,因此掌握一定的WebWork和Struts 1框架的知识,将对于掌握Struts 2框架大有裨益。特别是WebWork方面的开发经验,绝大部分都可以适用于Struts 2应用的开发。本章将会简要介绍Struts 1和WebWork框架的使用。
本章还将介绍在Eclipse IDE工具中开发Struts 2应用,并且详细介绍Struts 2配置文件的各个细节。
3.1 Struts 1的MVC实现
Struts 1是世界上最“古老”的MVC框架,它出现于2001年。Struts 1以ActionServlet作为核心控制器,由ActionServlet负责拦截用户的所有请求。Struts 1框架有3个重要组成部分:Action、ActionForm和ActionForward对象。
3.1.1 下载和安装Struts 1框架
本节的示例程序基于Struts 1的1.2.9版,请读者也下载该版本的Struts 1框架,下载和安装Struts 1框架请按如下步骤进行。
登录struts.apache.org站点,下载Struts的合适版本。解压缩下载到的压缩包,解压缩后,发现有如下文件结构:
— contrib:该路径下存放了Struts 1表达式语言支持的类库和标签库文件。
— lib:该路径下存放了Struts 1框架的核心类库。
— webapps:该路径下存放了Struts 1框架的示例应用及文档应用。
— 另外还一些关于Struts的说明和LICENSE等文档
将lib下的JAR文件全部复制到WEB-INF/lib下,并将TLD文件全部复制到WEB-INF下,再将validator-rules.xml文件也复制到WEB-INF下。如果需要用到Struts-EL(表达式)语言,还应将contrib下的JAR文件复制到WEB-INF/lib下。
为了在编译程序时可以导入Struts 1类库,还应将struts.jar文件增加到环境变量中。
3.1.2 实现ActionForm
Struts 1使用单独的ActionForm封装请求参数,当用户提交一个请求时,Struts 1将使用单独的ActionForm来封装所有的请求参数。从结构上看,ActionForm就是一个POJO:它需要为用户的每个请求参数都提供一个对应的属性,并为该属性设置相应的setter和getter方法——但ActionForm的设计不是POJO,它必须继承Struts 1的ActionForm基类。
下面是一个简单的登录页面,包含两个表单域,分别代表用户名和密码。该登录页面的代码如下:
<%@ page language="java" contentType="text/html;charset=gb2312" errorPage=""%>
<!-- 导入Struts的三个标签库-->
<%@include file="taglibs.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<!-- 使用国际化资源文件的key输出标题-->
<title><bean:message key="login"/></title>
</head>
<body>
<!-- 输出登录页面标题 -->
<h3><bean:message key="loginTitle"/></h3>
<!-- 输出Action处理完后的错误信息。 -->
<font color="red">${requestScope.err}</font>
<!-- 通过messagePresent判断输入校验后的出错提示是否存在 -->
<logic:messagesPresent>
<!-- 输出出错提示 -->
<bean:message key="errors.header"/>
<ul>
<html:messages id="error">
<li><bean:write name="error"/></li>
</html:messages>
</ul><hr />
</logic:messagesPresent>
<!-- 下面是登录表单 -->
<html:form action="login.do">
<!-- 使用国际化信息来输出标签 -->
<bean:message key="username"/><html:text property="username"/><br>
<bean:message key="pass"/><html:password property="pass"/><br>
<html:submit><bean:message key="login"/></html:submit><br>
</html:form>
</body>
</html>
在浏览器中浏览该页面时,看到该页面包含了一个简单的登录表单。该页面的界面如图3.1所示。
该页面中有两个表单域,这两个表单域封装了需要向服务器发送的请求参数,对于Strurts应用而言,请求参数是通过ActionForm来封装的,ActionForm非常类似一个POJO(定义几个属性,为每个属性提供setter和getter方法),但该ActionFrom需要继承ActionForm,或者是它的子类。因为本应用使用了Struts的校验框架,因此本应用中的ActionForm继承了ValidatorForm类。下面是本应用中ActionForm类的代码:
public class LoginForm extends ValidatorForm
{
//下面两个属性用于封装两个表单域的请求参数
private String username;
private String pass;
//以下是属性字段的系列get、set方法
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPass()
{
return pass;
}
public void setPass(String pass)
{
this.pass = pass;
}
}
上面ActionForm类的代码非常简单,除了该类需要继承ValidatorForm之外。也正是因为该ActionForm继承了ValidatorForm,从而导致了该类的污染,降低了该类的代码复用。
对于Struts 1而言,ActionForm类是一个既烦琐又没有技术含量的类,写起来难免让程序员觉得意兴索然。虽然有很多IDE(集成开发环境)可以自动生成该ActionForm类,但Struts 1框架也感觉到大量重复书写该类是一个负担,后来提供了动态ActionForm,让人可以避免书写ActionForm类。
注意 Struts 1中的ActionForm类是一个非常简单的类,它实质上是一个普通的JavaBean,但必须继承ActionForm类。除此之外,ActionForm还可以使用动态FormBean,从而允许通过配置文件来定义ActionForm。
3.1.3 实现Action
Action就是用于处理用户请求的业务控制器,当用户请求发送到ActionServlet后,ActionServlet拦截到用户请求,将请求转发到系统的业务控制器处理。ActionServlet在转发用户请求时,会将请求参数封装成ActionForm实例,并将该 ActionForm实例转发给Action实例。
Action实例从ActionForm中取出用户请求参数,然后调用业务逻辑组件处理用户请求,并根据处理结果,调用不同的视图页面来呈现处理结果。
下面是系统的Action处理类代码。
public class LoginAction extends Action
{
//必须重写该execute方法,该方法用于处理用户请求
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws
Exception
{
//获取封装用户请求参数的ActionForm实例
LoginForm loginForm = (LoginForm)form;
//从ActionForm中取出用户请求参数
String username = loginForm.getUsername();
String pass = loginForm.getPass();
//处理用户请求
if (username != null && username.equals("scott"))
{
return mapping.findForward("welcome");
}
else
{
return mapping.findForward("error");
}
}
}
上面的Action代码非常简单,甚至没有调用任何业务逻辑组件,只是直接判断用户请求参数的用户名和密码是否为scott和tiger,如果用户名和密码正确,则返回welcome的ActionForward,否则返回error的ActionForward。
注意 ActionForward就是一个逻辑视图,通过在配置文件中定义ActionFoward的映射,完成逻辑视图名和实际视图资源之间的映射。
Struts 1的Action类与Struts 2的Action类有一定的类似性,都通过调用execute方法来处理用户请求。但最大的区别在于Struts 1 Action的execute方法与Servlet API耦合,但Struts 2 Action类的execute方法无需与Servlet API耦合。
3.1.4 配置Struts 1的Action
实现了Struts 1的Action后,还需要在struts-config.xml文件中配置Action,配置Action需要指定Action的实现类,以及Action处理请求的URL。
配置Action时,还应该配置该Action对应的ActionForm,每个ActionForm使用一个<form-bean.../>元素定义。定义Action和ActonForm之间的关联关系时,在定义Action的<action../>元素中通过name属性指定与此Action关联的ActionForm。
因为本应用还使用了Struts 1的数据校验框架,因此配置<action.../>元素时,还应该增加validate属性,并将该属性值设置成true;还需要增加input属性,该属性指向输入校验失败后转入的视图资源。
下面是本应用的struts-config.xml文件代码。
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Struts 1配置文件的DTD信息 -->
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://struts.apache.org/dtds/struts-config_1_2.dtd">
<!-- 指定Struts 1配置文件的根元素 -->
<struts-config>
<!-- 所有的ActionForm都应该在form-beans元素中定义 -->
<form-beans>
<!-- 每个form-bean定义一个ActionForm -->
<form-bean name="loginForm" type="lee.LoginForm"/>
</form-beans>
<!-- 所有的Action都放在action-mappings元素中定义 -->
<action-mappings>
<!-- 定义Action,其处理类为lee.LoginAction,对应的ActionForm为
loginForm -->
<action path="/login" type="lee.LoginAction" name="loginForm"
validate="true" scope="request" input="/login.jsp">
<!-- 当返回welcome的ActionForward时,转入welcome.jsp页 -->
<forward name="welcome" path="/welcome.jsp" />
<!-- 当返回error的ActionForward时,转入error.jsp页 -->
<forward name="error" path="/error.jsp"/>
</action>
</action-mappings>
<!-- 加载国际化资源文件 -->
<message-resources parameter="MyResource"/>
<!-- 加载Struts 1校验框架的插件 -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<!-- 指定数据校验的两个校验规则文件 -->
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml" />
<set-property property="stopOnFirstError" value="true"/>
</plug-in>
</struts-config>
正如前面看到的,页面中使用大量国际化标签来输出国际化消息,因此本应用必须加载相应的国际化资源文件,本应用的国际化资源文件的baseName是MyResource。指定应用的国际化资源文件通过<message-resources .../>元素指定。
本应用使用了Struts 1的校验框架来完成输入校验,因此在本应用中加载了两个校验规则文件,其中validator-rules.xml文件是一个系统规则文件,通常无需修改;validation.xml文件是开发者定义的规则文件,该文件定义了需要校验的表单应该满足的规则。
下面是校验规则文件的代码。
<?xml version="1.0" encoding="GBK"?>
<!-- 校验规则定义文件 -->
<!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation
//DTD Commons Validator Rules Configuration 1.1.3//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<!-- 校验规则文件的根元素 -->
<form-validation>
<formset>
<!-- 定义需要校验的ActionForm,定义该ActionForm为loginForm -->
<form name="loginForm">
<!-- 定义username表单域必须满足必填、模式匹配两个规则 -->
<field property="username" depends="required,mask">
<arg key="username" position="0"/>
<!-- 指定模式匹配在正则表达式 -->
<var>
<var-name>mask</var-name>
<var-value>^\w+$</var-value>
</var>
</field>
<!-- 定义pass表单域必须满足必填、最小长度两个规则 -->
<field property="pass" depends="required,minlength">
<arg key="pass" position="0"/>
<!-- 定义最小长度的规则 -->
<arg name="minlength" key="${var:minlength}" resource="false"
position="1"/>
<var>
<var-name>minlength</var-name>
<var-value>4</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
上面的规则文件定义了表单域的两个表单必须满足的规则:username是必填的,而且必须匹配^\w+$的正则表达式;pass是必填的,并且至少有4位。
定义了上面的校验规则后,如果用户的输入不能通过该输入校验,则系统自动转入配置<action .../>元素时指定的input属性所对应的视图资源。
输入校验的提示信息都保存在国际化资源文件中,因此使用输入校验时,通常都建议使用国际化资源文件来保存校验提示信息。
提示 使用输入校验时,通常建议使用国际化资源文件来保存校验失败的提示信息。
3.1.5 完成应用流程
经过上面步骤,可以非常清楚地看出上面示例应用的流程,用户进入系统的login.jsp页面,用户输入请求信息,发送请求。如果用户的请求参数不能通过输入校验,则系统将请求转发到login.jsp页面,否则请求将被转发到业务逻辑控制器Action处。
Action处理用户请求参数,如果处理结果为success的ActionForward,则进入welcome.jsp页面;如果处理结果为error的ActionForward,则进入error.jsp页面。
图3.2显示了该应用的顺序图。
图3.2 系统顺序图
如果用户输入的用户名正确、密码正确,用户登录成功,可以在welcome.jsp页面见到国际化的欢迎信息,并且通过Struts 1的标签输出登录的用户名。下面是welcome.jsp页面的代码。
<%@ page contentType="text/html;charset=gb2312" errorPage="error.jsp"%>
<!-- 导入Struts的三个标签库-->
<%@include file="taglibs.jsp"%>
<html>
<head>
<!-- 通过Struts 1标签输出国际化的页面标题 -->
<title><bean:message key="loginSuccess"/></title>
</head>
<body>
<h3><bean:message key="loginSuccess"/></h3>
<hr>
<!-- 通过Struts 1标签输出国际化的欢迎信息,并把登录用的用户名作为参数传入 -->
<bean:message key="welcome" arg0="${loginForm.username}"/>
</body>
</html>
如果用户登录成功,将看到如图3.3所示的页面。
图3.3 登录成功页面
上面的Struts 1应用已经包含了Struts 1框架的核心部分,如ActionForm、Action实现,数据校验完成,应用的国际化支持等。
通过上面的示例应用,我们看到Struts 1框架的最大缺点是:Struts 1应用的类与Servlet API耦合严重,而且大都需要继承Struts 1框架提供的基类,或实现Struts 1框架提供的接口,造成了代码污染。
3.2 WebWork的MVC实现
WebWork由OpenSymphony组织负责开发、维护,WebWork 2前身是Rickard Oberg开发的WebWork。现在我们所说的WebWork,通常指的是WebWork 2,它是由Xwork 1和WebWork 2两个项目组成的。
WebWork紧紧以ServletDispatcher为核心,使用ServletDispatcher来处理所有的用户请求,它是整个WebWork框架的核心控制器。WebWork支持多视图表示,视图部分可以使用JSP、Velocity、FreeMarker、JasperReports、XML等。WebWork当前的最新版本是2.2.5,本节所使用的范例是基于该版本的WebWork完成的。由于WebWork已经全面整合到Struts 2框架中,故WebWork不会再有新版本发布,即不会有WebWork 2.3.X发布。
3.2.1 WebWork的下载和安装
WebWork当前的最新稳定版本是2.2.5,笔者建议读者也下载该版本。WebWork的下载和安装按如下步骤进行。
登录http://www.opensymphony.com/webwork/站点,下载WebWork的最新稳定版,解压缩后发现如下的文件结构:
— dist:该路径下存放了WebWork的两个编译版本。
— docs:该路径下存放了WebWork的各种文档,包括WebWork的Wiki文档、API文档等。
— lib:该路径下存放了WebWork编译和运行所需的第三方类库,其中default路径下的所有JAR文件是WebWork应用必需的类库文件。
— src:该路径下存放了WebWork的源文件,如果使用WebWork过程中遇到无法解决的问题,可以参考该源文件解决。
— webapps:该路径下存放了WebWork的示例应用,这些示例应用是学习WebWork极好的资料。
— webwork-2.2.5.jar:该文件是WebWork框架的核心类库文件。
— webwork-src-2.2.5.jar:该压缩文件里包含了WebWork框架所有源代码。
— 此外还有一些WebWork框架的编译脚本文件及各种说明文件。
WebWork通常都作为Web框架使用,在Web应用中使用该框架。为Web应用增加WebWork框架的方法,与在Web应用中增加其他框架的方法完全一样。下面介绍如何安装WebWork框架。
将webwork-2.2.5.jar文件复制到Web应用的WEB-INF/lib路径下,再将lib\default路径下的*.jar文件全部复制到WEB-INF/lib下。
注意 使用WebWork框架必须有webwork-2.2.5.jar和lib\default路径下的全部JAR文件。至于lib路径下的其他JAR文件,则是可选的——只有当应用中需要使用这些第三方框架时,才需要这些类库文件。
为了可以编译WebWork应用程序,建议将webwork-2.2.5.jar增加到CLASSPATH的环境变量中。当然也可以使用Ant工具,则无需增加环境变量。
3.2.2 实现Action
用户需要实现的WebWork核心组件依然是业务控制器,业务控制器就是一个Action,该Action也包含一个execute方法,但该方法不再包含任何Servlet API,也不包含任何WebWork API。这个Action与Struts 1的Action不同,它非常类似于一个普通的POJO。
本应用的Action代码如下:
public class LoginAction extends ActionSupport
{
//下面两个属性用于封装用户请求参数
private String user;
private String pass;
//如下请求参数用于封装Action处理请求后的反馈
private String tip;
//user属性对应的setter和getter方法
public String getUser()
{
return user;
}
public void setUser(String user)
{
this.user = user;
}
//pass属性对应的setter和getter方法
public String getPass()
{
return pass;
}
public void setPass(String pass)
{
this.pass = pass;
}
//tip属性对应的setter和getter方法
public String getTip()
{
return tip;
}
//tip属性对应的setter和getter方法
public void setTip(String tip)
{
this.tip = tip;
}
//处理用户请求的execute方法
public String execute() throws Exception
{
if (getUser().equals("scott")
&& getPass().equals("tiger"))
{
setTip(getText("loginSuccess"));
return SUCCESS;
}
else
{
setTip(getText("loginFail"));
return ERROR;
}
}
}
上面Action类的代码与Struts 1的Action类存在如下几个区别:
— WebWork的Action类无需与Servlet API耦合,从而可以更方便地进行单元测试。
— WebWork的Action类可无需ActionForm的支持,它本身包含了两个属性用于封装用户的请求参数。
— WebWork的Action类里包含的属性不仅可以封装用户请求参数,还可以封装Action的处理结果。
— WebWork的Action的execute方法返回的逻辑视图是一个标准字符串,而不是一个ActionForward对象。
本应用中的Action继承了WebWork的ActionSupport基类,继承该基类导致了Action的代码污染,WebWork不强制要求Action类继承ActionSupport基类,但继承该基类可以更便捷地实现Action类,例如程序国际化。
上面的Action实现类中包含了两个国际化信息,这两个国际化信息对应的key分别为loginSuccess和loginFail。为了在Action类中访问这两个国际化信息,需要在Action类中使用getText方法,该方法可以输出对应key的国际化信息。该getText方法正是来自于ActionSupport基类的方法。
注意 WebWork的Action类不强制要求使用ActionForm类封装用户请求参数。但可使用模型驱动模式,在模型驱动模式下,Action不再负责封装请求参数,也不负责封装Action的处理结果。而是将请求参数和处理结果放在模型中封装——模型就是一个类似于ActionForm的对象,但该对象是一个POJO,无需继承任何WebWork基类。相对于直接使用Action封装请求参数的模式,模型驱动模式的编程更加复杂,因为需要额外增加一个模型类;但模型驱动模式更易理解:Action专门负责处理用户请求,而模型则专门负责封装数据。
因为上面的Action封装了user和pass两个请求参数,故对应的JSP页面也应该包含这样两个表单域。下面是对应JSP页面的代码。
<%@ page contentType="text/html;charset=GBK"%>
<!-- 导入WebWork的标签库文件 -->
<%@ taglib prefix="ww" uri="/webwork"%>
<html>
<head>
<!-- 使用国际化信息标签输出国际化标题 -->
<title><ww:text name="loginTitle"/></title>
</head>
<body>
<ww:text name="loginTitle"/>
<hr>
<!-- 使用WebWork标签生成登录表单 -->
<ww:form method="post" action="login">
<!-- 生成登录用的用户名文本框 -->
<ww:textfield name="user" label="%{getText('user')}"/>
<!-- 生成登录用的密码文本框 -->
<ww:textfield name="pass" label="%{getText('pass')}"/>
<!-- 生成登录按钮 -->
<ww:submit value="%{getText('submit')}"/>
</ww:form>
</body>
</html>
读者可能已经注意到,上面的表单定义中,大量使用了WebWork的标签库,借助于WebWork标签库,可以更简单的生成页面的表单元素。因此,上面页面中的表单定义中,甚至无需为表单域指定对应的Label信息,但可生成如下表单定义。图3.4显示了在浏览器中浏览该页面的效果。
当使用<ww:textfield name="user" label="%{getText ('user')}"/>来生成一个文本框时,该标签负责生成一个名为user的文本输入框,该输入框对应的Label通过%{getText('user')表达式生成输出,该表达式负责输出一个国际化信息,该国际化信息对应的key为user。
3.2.3 配置Action
实现上面的Action后,需要在应用中配置该Action。WebWork使用一个xwork.xml文件来配置Action。
在配置WebWork的Action之前,需要先配置Web应用加载WebWork框架。让Web应用加载WebWork框架就是配置WebWork的核心控制器,WebWork的核心控制器是ServletDispatcher。
因为Web应用默认只加载web.xml文件,因此为了加载WebWork框架,必须在web.xml文件中配置WebWork的核心控制器:ServletDispatcher。修改后的web.xml文件如下:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Web应用配置文件的Schema信息 -->
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.
com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<!-- 配置WebWork的核心Servlet -->
<servlet>
<!-- 指定WebWork核心Servlet的名字 -->
<servlet-name>webwork</servlet-name>
<!-- 指定WebWork核心Servlet的实现类 -->
<servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- 配置WebWork核心Servlet的映射 -->
<servlet-mapping>
<!-- 配置WebWork核心Servlet映射的URL -->
<servlet-name>webwork</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
指定了上面的web.xml配置文件后,Web应用会自动加载WebWork框架,并加载Web应用的WEB-INF/classes路径下的xwork.xml文件。xwork.xml文件就是WebWork的配置文件,该文件主要配置应用的Action,以及Action对应的URL。
<?xml version="1.0" encoding="GBK"?>
<!-- 下面指定了WebWork配置文件的DTD信息 -->
<!DOCTYPE xwork PUBLIC
"-//OpenSymphony Group//XWork 1.1//EN"
"http://www.opensymphony.com/xwork/xwork-1.1.dtd">
<!-- xwork是WebWork配置文件的根元素 -->
<xwork>
<!-- 导入WebWork默认配置文件 -->
<include file="webwork-default.xml" />
<!-- WebWork默认将Action配置放在package下管理 -->
<package name="default" extends="webwork-default">
<!-- 指定login的Action,该Action的实现类为lee.LoginAction -->
<action name="Login" class="lee.LoginAction">
<!-- 指定input逻辑视图对应的物理视图资源 -->
<result name="input">/login.jsp</result>
<!-- 指定error逻辑视图对应的物理视图资源 -->
<result name="error">/error.jsp</result>
<!-- 指定success逻辑视图对应的物理视图资源 -->
<result name="success">/success.jsp</result>
</action>
</package>
</xwork>
在上面的xwork.xml配置文件中,所有的Action放在package元素下管理,因为WebWork以包来管理所有的Action,所以全部的action元素都作为package元素的子元素存在。
注意 WebWork以package来管理WebWork的Action,故所有的action元素都是package元素的子元素。
配置action元素时,必须指定两个主要属性:name属性和class属性。其中name属性指定了该Action负责处理的URL,而class属性指定了该Action对应的实现类。例如上面的Action配置,该Action的name为login,表示该Action负责处理向login.action的URL发送的请求,处理请求的Java类为lee.LoginAction。
实际上,WebWork可以包含两个配置文件,还有一个webwork.properties配置文件,这个文件是一个标准的属性文件,由一系列的WebWork属性的key和value组成。下面是webwork.properties文件的代码。
#指定该WebWork应用处于开发模式下,如果出错,将输出更多出错调试信息
webwork.devMode = true
#指定每次请求都重载WebWork的配置文件
webwork.configuration.xml.reload=true
#指定WebWork应用的国际化资源文件的baseName为mess
webwork.custom.i18n.resources=mess
因为本应用处于开发阶段,故设置了WebWork应用的开发模式为true,并且每次请求都会重载WebWork的配置文件。上面配置文件的最后一行指定了WebWork的国际化资源文件的baseName为mess。
上面配置WebWork应用时,指定了Action处理后逻辑视图名和物理视图之间的对应关系:"input"字符串对应/login.jsp页面,"error"字符串对应/error.jsp页面,"success"字符串对应/success.jsp页面。对比上面的Action类代码,发现该类的execute方法分别返回了SUCCESS和ERROR两个字符串常量——它们的定义包含在ActionSupport类中,其值分别为"success"和"error"字符串。
这就是本系统流程:当用户输入的用户名为scott,且密码为tiger时,Action返回一个success的逻辑视图,该逻辑视图对应为/success.jsp页面;否则将进入/error.jsp页面。那么input逻辑视图的作用呢?input逻辑视图是输入校验失败后默认转入的视图页面。
提示 与Struts 1类似,WebWork采用input逻辑视图来作为输入校验失败后转入的视图。
3.2.4 完成数据校验
WebWork可以支持手动完成输入校验,但这不是本应用将采用的方式,因为手动完成输入校验需要重写Action类的validate()方法,需要大量书写重复代码,相当烦琐。本应用利用了XWork的校验框架来完成数据校验,完成数据校验只需要在Action class文件所在的路径下存放对应的校验文件即可。
下面是本应用的数据校验文件代码。
<?xml version="1.0" encoding="gb2312"?>
<!-- WebWork校验文件的DTD信息 -->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork
Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<!-- validators是校验文件的根元素 -->
<validators>
<!-- 使用表单校验器校验user表单域 -->
<field name="user">
<!-- 指定user表单域必须满足requiredstring(必填)规则 -->
<field-validator type="requiredstring">
<!-- 违反输入校验后的提示信息 -->
<message key="user.required"/>
</field-validator>
</field>
<!-- 使用表单校验器校验pass表单域 -->
<field name="pass">
<!-- 指定pass表单域必须满足requiredstring(必填)规则 -->
<field-validator type="requiredstring">
<message key="pass.required"/>
</field-validator>
<!-- 指定pass表单域必须满足stringlength(长度范围)规则 -->
<field-validator type="stringlength">
<!-- 指定最小长度 -->
<param name="minLength">3</param>
<!-- 指定最大长度 -->
<param name="maxLength">6</param>
<message key="pass.length"/>
</field-validator>
</field>
</validators>
上面的校验文件使用了两个表单校验器:requiredstring和stringlength,分别对应用的两个表单域进行校验,分别校验用户名必须满足必填规则,密码必须满足必填且字符串长度必须在3和6之间的规则。
如果用户输入不满足该规则,则看到如图3.5所示的界面。
图3.5 输入校验的效果
通过上面的示范代码,可以看出,结合XWork的校验框架,可以大大简化WebWork应用的数据校验。
对比上面的Struts 1框架和WebWork框架的使用,不难发现WebWork与Struts 2的设计更类似。
3.2.5 完成应用流程
正如我们见到的,如果我们输入了合适的用户名和密码,Action处理完用户请求后,将会封装一个名为tip的属性在结果里,并转入success的逻辑视图,该逻辑视图对应了/success.jsp页面。该页面的代码非常简单,它只是简单地使用WebWork标签来输出tip的提示信息。页面代码如下:
<%@ page contentType="text/html;charset=GBK"%>
<!-- 导入WebWork的标签库 -->
<%@ taglib prefix="ww" uri="/webwork"%>
<html>
<head>
<!-- 输出tip提示信息 -->
<title><ww:property value="tip"/></title>
</head>
<body>
<h3><ww:property value="tip"/></h3>
<!-- 使用WebWork的国际化标签来输出国际化提示 -->
<ww:text name="welcomeMsg">
<!-- 传入国际化提示信息的参数 -->
<ww:param value="user"/>
</ww:text>
</body>
</html>
在上面的页面中,我们使用了<ww.text .../>标签来输出国际化消息,该标签输出的国际化消息如下:
welcomeMsg=欢迎您,{0},您已经成功登录本系统!
看到上面的国际化消息需要传入一个参数,我们通过为<ww:text .../>标签指定一个<ww:param .../>子标签来传入参数:第一个<ww:param .../>子元素为第一个参数指定值,第二个<ww:param .../>子元素为第二个参数指定值……依此类推。
上面代码使用了<ww:param value="user"/>来传入参数,该参数的值就是Action中user属性值。当用户输入的用户名和密码正确时,将看到如图3.6所示的界面。
图3.6 登录成功的界面
3.3 在Eclipse中开发Struts 2
前面已经详细介绍了如何开发一个Struts 2应用,但没有使用任何IDE工具,那种开发方式虽然能让读者更清楚开发的细节,但对于开发效率却不如使用IDE工具的开发效率高。本节将以Eclipse为例,介绍如何在IDE工具中开发Struts 2应用。
3.3.1 创建Web应用
笔者所使用的Eclipse已经安装了MyEclipse插件,因此可以很方便地建立一个Web应用,并将其部署在Web服务器中。
下面以Tomcat 5520为例,介绍如何在Eclipse中整合Tomcat服务器,新建一个Web应用,并将该应用部署到Tomcat服务器中。
现在开始在Eclipse中整合Tomcat服务器,实际上,如果我们安装了MyEclipse插件,则可以在Eclipse中整合大部分的Java EE服务器(包括Web服务器)。但如果我们没安装任何插件,Eclipse则很难与任何Java EE服务器整合。
提示 Eclipse必须安装了相应插件后才具有丰富的功能。为了开发Java EE应用,通常都推荐安装MyEclipse插件,该插件是一个商业产品,必须支付相应费用才可使用。
为了在Eclipse中整合Tomcat服务器,请按如下步骤进行。
单击Eclipse窗口里的“Window”菜单,在出现的“Window”的系列菜单项中选择“Preferences”菜单项,将弹出如图3.7所示的界面。
该Preferences设置窗口是Eclipse大部分参数的设置窗口,该窗口还可设置Eclipse插件的相关属性。MyEclipse插件的相关属性就是在该窗口里设置的,而整合Java EE服务器只是MyEclipse插件的相关属性。
单击图3.7所示窗口左边导航树中的“MyEclipse”节点,然后单击“MyEclipse”节点下的“Application Servers”子节点,将看到如图3.8所示的界面。
图3.7 Eclipse的Preferences设置窗口
图3.8 在Eclipse中整合应用服务器
拖动图3.8所示窗口左边的垂直滚动条,我们可以发现大部分应用服务器的名字,例如Jboss、Jetty、Resin等,当然也可以发现我们需要使用的Tomcat节点,然后单击Tomcat 5节点,出现如图3.9所示的界面。
图3.9 整合Tomcat5
在图3.9右上方看到两个单选框:Enable和Disable,为了整合Tomcat,显然应该选中Enable单选框。选中该单选框后,还应该在下面的三个文本输入框内输入相应的地址,例如,Tomcat Home Directory里应该输入Tomcat的安装路径,笔者的Tomcat安装在D盘的tomcat5520路径下,故在该文本框内输入了D:\tomcat5520的内容。通常,一旦我们输入了正确的Tomcat安装路径,下面的两个文本框会自动输入,无需用户干预。
单击图3.9所示窗口右下方的“OK”按钮,返回Eclipse主界面,Eclipse和Tomcat的整合成功。
下面介绍如何在Eclipse中建立一个Web应用,在Eclipse中建立一个Web应用也可借助于MyEclipse的帮助。借助于MyEclipse建立Web应用按如下步骤进行。
单击Eclipse主界面中的“File”菜单,在出现的菜单中单击“New”下的“Other...”菜单项,将弹出一个如图3.10所示的对话框。
在如图3.10所示的对话框中,选中“Web Project”节点,表明将要创建一个Web应用,然后单击“Next”按钮,看到如图3.11所示的对话框。
图3.10 新建Web应用的对话框 图3.11 新建Web应用
在如图3.11所示对话框的Project Name文本框中输入应用的名字,本应用使用Struts2Demo作为应用的名字,输入完成后可看到如图3.11所示的对话框。单击“Finish”按钮,新建Web应用成功。
3.3.2 增加Struts 2支持
为了让Web应用具有Struts 2支持功能,必须将Struts 2框架的核心类库增加到Web应用中。将Struts 2框架下lib路径下的struts2-core-2.0.6.jar、xwork-2.0.1.jar等Struts 2框架的核心类库复制到Web应用的lib路径下,也就是“%workspace%Struts2Demo\WebRoot\ WEB-INF\lib”路径下。
返回Eclipse的主界面,在Eclipse主界面的左上角资源导航中看到了struts2Demo节点,选中该节点,然后按F5键,将看到Eclipse主界面左上角资源导航中出现如图3.12所示的界面。
看到如图3.12所示的界面,表明该Web应用已经加入了Struts 2的核心类库,但还需要修改web.xml文件,让该文件负责加载Struts 2框架。
在如图3.12所示的导航树中,单击“WEB-INF”节点前的加号,展开该节点,看到该节点下包含的web.xml文件子节点。
单击web.xml文件节点,编辑该文件,编辑该文件没有丝毫特殊之处,同样是在web.xml文件中定义Struts 2的核心Filter,并定义该Filter所拦截的URL 模式。至此,该Web应用完全具备了Struts 2框架的支持。
3.3.3 部署Struts 2应用
本应用的功能非常类似于第2章所介绍的Struts 2应用,因此本节所使用的JSP页面与第2章所使用的JSP页面也非常类似,故此处不再给出这些JSP页面的代码。
在Eclipse中添加JSP页面也是很简单的,此处不再赘述。
建立了这些JSP页面后,单击Eclipse主界面上部署Web应用的工具按钮,部署Web应用和启动服务器的两个按钮如图3.13所示。
图3.13 在Eclipse中部署Web应用和启动服务器的按钮
在Eclipse中部署Web应用请按如下步骤进行。
单击部署Web应用的按钮,弹出如图3.14所示的对话框。
图3.14 部署Web应用的对话框
在图3.14所示对话框的上面看到选择项目的下拉列表,选中需要部署的Web应用,例如本示例就是Struts2Demo。单击右边的“Add”按钮,该按钮用于添加想要部署到的Web服务器,将看到如图3.15所示的对话框。
图3.15 部署到应用服务器
在图3.15所示对话框上面的Server下拉列表中选中“Tomcat 5”选项,其他选项设置参照图3.15,然后单击“Finish”按钮,返回图3.14所示对话框,单击“OK”按钮,Web应用部署成功。
Web应用部署成功后,单击如图3.13所示工具条中的“启动服务器”按钮旁的小三角,出现下拉菜单,选择“Tomcat 5”下的“Start”菜单项,如图3.16所示,启动Tomcat服务器。
在浏览器中访问刚才的Struts2Demo应用,将可看到本应用登录页面。以Tomcat的端口为8888为例,应该在浏览器中访问如下地址:http://localhost:8888/Struts2Demo/ login.jsp,将看到如图3.17所示的界面。
图3.16 启动服务器 图3.17 部署成功后的登录界面
在图3.17中看到该登录页面的文本框没有任何提示信息,按钮上也没有文本,这是因为本应用缺少国际化资源文件。增加该应用所需的国际化资源文件,该资源文件与前面应用的资源文件相同,此处不再给出。
为了让Struts 2应用加载该国际化资源文件,还应该使用struts.properties文件,该文件内指定如下一行代码:
#指定Struts 2的国际化资源文件的baseName为messageResource
struts.custom.i18n.resources=messageResource
至此,Struts 2应用已经获得了国际化支持。如果在浏览器中再次浏览该页面,将看到如图3.18所示的界面。
3.3.4 增加应用的Action
在Eclipse工具中新建一个Java类,该Java类的类名为“LoginAction”,其代码与第2章所使用的LoginAction类代码相同,将该类文件保存在Struts2Demo应用的src路径下的lee目录下。
图3.18 增加了国际化资源文件后的登录页面
新建一个Java文件的方法非常简单,此处不再赘述。本应用的Action类的代码与第2章的Action类完全一样,此处也不再给出。
增加了Struts 2的Action类后,还需要增加对应的配置文件,也是通过单击Eclipse的“File”菜单,然后单击“New”下的“File”菜单项来操作的。
提示 因为Eclipse暂时还没有专门的Struts 2插件,因此只能用最原始的方式来建立一个Struts 2的配置文件。等到专门的Struts 2插件被开发出来后,应该可以有更简单的方法来建立Struts 2的配置文件。
建立该文件后,为该文件增加Struts 2的Action定义,定义该Action与前面介绍的基本一致,需要定义Action的name属性、class属性等。增加Action定义后的struts.xml文件代码如下:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Struts 2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- 指定Struts 2配置文件的根元素 -->
<struts>
<!-- 所有的Action定义都应该放在package下 -->
<package name="lee" extends="struts-default">
<!-- 定义Action -->
<action name="Login" class="lee.LoginAction">
<!-- 定义三个逻辑视图和物理资源之间的映射 -->
<result name="input">/login.jsp</result>
<result name="error">/error.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
</package>
</struts>
至此,整个Struts 2应用完全建立成功,如果读者还需要增加数据校验,则可增加对应的校验文件,并将该文件放在与对应Action源文件的同一路径下即可。
3.4 Struts 2的基本流程
经过前面介绍,我们已经基本了解了Struts 2框架的MVC实现。大致上,Struts 2框架由3个部分组成:核心控制器FilterDispatcher、业务控制器和用户实现的业务逻辑组件。在这3个部分里,Struts 2框架提供了核心控制器FilterDispatcher,而用户需要实现业务控制器和业务逻辑组件。
3.4.1 核心控制器:FilterDispatcher
FilterDispatcher是Struts 2框架的核心控制器,该控制器作为一个Filter运行在Web应用中,它负责拦截所有的用户请求,当用户请求到达时,该Filter会过滤用户请求。如果用户请求以action结尾,该请求将被转入Struts 2框架处理。
Struts 2框架获得了*.action请求后,将根据*.action请求的前面部分决定调用哪个业务逻辑组件,例如,对于login.action请求,Struts 2调用名为login的Action来处理该请求。
Struts 2应用中的Action都被定义在struts.xml文件中,在该文件中定义Action时,定义了该Action的name属性和class属性,其中name属性决定了该Action处理哪个用户请求,而class属性决定了该Action的实现类。
Struts 2用于处理用户请求的Action实例,并不是用户实现的业务控制器,而是Action代理——因为用户实现的业务控制器并没有与Servlet API耦合,显然无法处理用户请求。而Struts 2框架提供了系列拦截器,该系列拦截器负责将HttpServletRequest请求中的请求参数解析出来,传入到Action中,并回调Action的execute方法来处理用户请求。
显然,上面的处理过程是典型的AOP(面向切面编程)处理方式。图3.19显示了这种处理模型。
图3.19 Struts 2的拦截器和Action
从图3.19中可以看出,用户实现的Action类仅仅是Struts 2的Action代理的代理目标。用户实现的业务控制器(Action)则包含了对用户请求的处理。用户的请求数据包含在HttpServletRequest对象里,而用户的Action类无需访问HttpServletRequest对象。拦截器负责将HttpServletRequest里的请求数据解析出来,并传给业务逻辑组件Action实例。
3.4.2 业务控制器
正如从图3.19所看到的,业务控制器组件就是用户实现Action类的实例,Action类里通常包含了一个execute方法,该方法返回一个字符串——该字符串就是一个逻辑视图名,当业务控制器处理完用户请求后,根据处理结果不同,execute方法返回不同字符串 ——每个字符串对应一个视图名。
程序员开发出系统所需要的业务控制器后,还需要配置Struts 2的Action,即需要配置Action的如下三个部分定义:
— Action所处理的URL。
— Action组件所对应的实现类。
— Action里包含的逻辑视图和物理资源之间的对应关系。
每个Action都要处理一个用户请求,而用户请求总是包含了指定URL。当Filter Dispatcher拦截到用户请求后,根据请求的URL和Action处理URL之间的对应关系来处理转发。
3.4.3 Struts 2的模型组件
实际上,模型组件已经超出了MVC框架的覆盖范围。对于Struts 2框架而言,通常没有为模型组件的实现提供太多的帮助。
Java EE应用里的模型组件,通常指系统的业务逻辑组件。而隐藏在系统的业务逻辑组件下面的,可能还包含了DAO、领域对象等组件。
通常,MVC框架里的业务控制器会调用模型组件的方法来处理用户请求。也就是说,业务逻辑控制器不会对用户请求进行任何实际处理,用户请求最终由模型组件负责处理。业务控制器只是中间负责调度的调度器,这也是称Action为控制器的原因。
图3.20显示了这种处理流程。
提示 在图3.20中看到Action调用业务逻辑组件的方法。当控制器需要获得业务逻辑组件实例时,通常并不会直接获取业务逻辑组件实例,而是通过工厂模式来获得业务逻辑组件的实例;或者利用其他IoC容器(如Spring容器)来管理业务逻辑组件的实例。
3.4.4 Struts 2的视图组件
Struts 2已经改变了Struts 1只能使用JSP作为视图技术的现状,Struts 2允许使用其他的模板技术,如FreeMarker、Velocity作为视图技术。
当Struts 2的控制器返回逻辑视图名时,逻辑视图并未与任何的视图技术关联,仅仅是返回一个字符串,该字符串作为逻辑视图名。
当我们在struts.xml文件中配置Action时,不仅需要指定Action的name属性和class属性,还要为Action元素指定系列result子元素,每个result子元素定义一个逻辑视图和物理视图之间的映射。前面所介绍的应用都使用了JSP技术作为视图,故配置result子元素时没有指定type属性,默认使用JSP作为视图资源。
如果需要在Struts 2中使用其他视图技术,则可以在配置result子元素时,指定相应的type属性即可。例如,如果需要使用FreeMarker,则为result指定值为freemarker的type属性;如果想使用Velocity模板技术作为视图资源,则为result指定值为velocity的type属性……
3.4.5 Struts 2的运行流程
经过上面介绍,我们发现Struts 2框架的运行流程非常类似于WebWork框架的流程。
提示 在Struts 2的官方站点,我们可以找到如下说法:Essentially,Struts 2.0 is the technical equivalent of WebWork 2.3。Aside from the package and property renaming,it isn't much different than,say,migrating from WebWork 2.1 to 2.2——意思是说:Struts 2.0技术等同于WebWork 2.3框架,除了包和属性被改名外。从WebWork 2.2迁移到Struts 2不会比从WebWork 2.1迁移到WebWork 2.2更复杂。
这里我们可以看到,Struts 2其实就是WebWork 2.2的升级版,这也就不难理解:为什么WebWork和Struts 2如此相似!
因此,Struts 2的运行流程与WebWork的运行流程完全相同,读者可以参看图1.8来了解Struts 2的运行流程。
3.5 Struts 2的基本配置
前面大致了解了Struts 2框架的基本内容,但这些基本内容都必须建立在Struts 2的配置文件基础之上,这些配置文件的配置信息也是Struts 2应用的核心部分。
3.5.1 配置web.xml文件
任何MVC框架都需要与Web应用整合,这就不得不借助于web.xml文件,只有配置在web.xml文件中Servlet才会被应用加载。
通常,所有的MVC框架都需要Web应用加载一个核心控制器,对于Struts 2框架而言,需要加载FilterDispatcher,只要Web应用负责加载FilterDispatcher,FilterDispatcher将会加载应用的Struts 2框架。
因为Struts 2将核心控制器设计成Filter,而不是一个普通Servlet。故为了让Web应用加载FilterDispacher,只需要在web.xml文件中配置FilterDispatcher即可。
配置FilterDispatcher的代码片段如下:
<!-- 配置Struts 2框架的核心Filter -->
<filter>
<!-- 配置Struts 2核心Filter的名字 -->
<filter-name>struts</filter-name>
<!-- 配置Struts 2核心Filter的实现类 -->
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
<init-param>
<!-- 配置Struts 2框架默认加载的Action包结构 -->
<param-name>actionPackages</param-name>
<param-value>org.apache.struts2.showcase.person</param-value>
</init-param>
<!-- 配置Struts 2框架的配置提供者类 -->
<init-param>
<param-name>configProviders </param-name>
<param-value>lee.MyConfigurationProvider</param-value>
</init-param>
</filter>
正如上面看到的,当配置Struts 2的FilterDispatcher类时,可以指定一系列的初始化参数,为该Filter配置初始化参数时,其中有3个初始化参数有特殊意义:
— config:该参数的值是一个以英文逗号(,)隔开的字符串,每个字符串都是一个XML配置文件的位置。Struts 2框架将自动加载该属性指定的系列配置文件。
— actionPackages:该参数的值也是一个以英文逗号(,)隔开的字符串,每个字符串都是一个包空间,Struts 2框架将扫描这些包空间下的Action类。
— configProviders:如果用户需要实现自己的ConfigurationProvider类,用户可以提供一个或多个实现了ConfigurationProvider接口的类,然后将这些类的类名设置成该属性的值,多个类名之间以英文逗号(,)隔开。
除此之外,还可在此处配置Struts 2常量,每个<init-param>元素配置一个Struts 2常量,其中<param-name>子元素指定了常量name,而<param-value>子元素指定了常量value。
提示 关于Struts 2常量的讲解,请参阅本书的4.1.2节。
在web.xml文件中配置了该Filter,还需要配置该Filter拦截的URL。通常,我们让该Filter拦截所有的用户请求,因此使用通配符来配置该Filter拦截的URL。
下面是配置该Filter拦截URL的配置片段:
<!-- 配置Filter拦截的URL -->
<filter-mapping>
<!-- 配置Struts 2的核心FilterDispatcher拦截所有用户请求 -->
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置了Struts 2的核心FilterDispatcher后,基本完成了Struts 2在web.xml文件中的配置。
如果Web应用使用了Servlet 2.3以前的规范,因为Web应用不会自动加载Struts 2框架的标签文件,因此必须在web.xml文件中配置加载Struts 2标签库。
配置加载Struts 2标签库的配置片段如下:
<!-- 手动配置Struts 2的标签库 -->
<taglib>
<!-- 配置Struts 2标签库的URI -->
<taglib-uri>/s</taglib-uri>
<!-- 指定Struts 2标签库定义文件的位置 -->
<taglib-location>/WEB-INF/struts-tags.tld</taglib-location>
</taglib>
在上面配置片段中,指定了Struts 2标签库配置文件物理位置:/WEB-INF/struts- tags.tld,因此我们必须手动复制Struts 2的标签库定义文件,将该文件放置在Web应用的WEB-INF路径下。
如果Web应用使用Servlet 2.4以上的规范,则无需在web.xml文件中配置标签库定义,因为Servlet 2.4规范会自动加载标签库定义文件。
提示 Struts 2的标签库定义文件包含在struts2-core-2.0.6.jar文件里,在struts2-core-2.0.6.jar文件的META-INF路径下,包含了一个struts-tag.tld文件,这个文件就是Struts 2的标签库定义文件,Servlet 2.4规范会自动加载该标签库文件。
对于Servlet 2.4以上的规范,Web应用自动加载该标签库定义文件。加载struts-tag.tld标签库定义文件时,该文件的开始部分包含如下代码片段:
<taglib>
<!-- 定义标签库的版本 -->
<tlib-version>2.2.3</tlib-version>
<!-- 定义标签库所需的JSP版 -->
<jsp-version>1.2</jsp-version>
<short-name>s</short-name>
<!-- 定义Struts 2标签库的URI -->
<uri>/struts-tags</uri>
...
</taglib>
因为该文件中已经定义了该标签库的URI:struts-tags,这就避免了在web.xml文件中重新定义Struts 2标签库文件的URI。
3.5.2 struts.xml配置文件
Struts框架的核心配置文件就是struts.xml配置文件,该文件主要负责管理Struts 2框架的业务控制器Action。
在默认情况下,Struts 2框架将自动加载放在WEB-INF/classes路径下的struts.xml文件。在大部分应用里,随着应用规模的增加,系统中Action数量也大量增加,导致struts.xml配置文件变得非常臃肿。
为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。
下面的struts.xml文件中就通过include手动导入了一个配置文件:struts-part1.xml文件,通过这种方式,就可以将Struts 2的Action按模块配置在多个配置文件中。
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 指定Struts 2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- 下面是Struts 2配置文件的根元素 -->
<struts>
<!-- 通过include元素导入其他配置文件 -->
<include file="struts-part1.xml" />
...
</struts>
通过这种方式,Struts 2提供了一种模块化的方式来管理struts.xml配置文件。
除此之外,Struts 2还提供了一种插件式的方式来管理配置文件。用WinRAR等解压缩软件打开struts2-core-2.0.6.jar文件,看到如图3.21所示的文件结构,在光标选中的一行,看到有一个struts-default.xml文件。
图3.21 struts2-core-2.0.6.jar压缩文件的文件结构
查看struts-default.xml文件,看到该文件代码片段如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 指定Struts 2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- Struts 2配置文件的根元素 -->
<struts>
<!-- 下面定义了Struts 2框架的一些基础Bean -->
<bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts"
class="org.apache.struts2.impl.StrutsObjectFactory" />
....
<!-- 下面是一些静态注入Bean定义 -->
<bean class="com.opensymphony.xwork2.util.OgnlValueStack" static="true" />
<bean class="org.apache.struts2.dispatcher.Dispatcher" static="true" />
...
<!-- 下面定义Struts 2的默认包空间 -->
<package name="struts-default">
<!-- 定义Struts 2内建支持的结果类型 -->
<result-types>
<!-- 定义Action链Result类型 -->
<result-type name="chain" class="com.opensymphony.xwork2.
ActionChainResult"/>
<!-- 定义Dispatcher的Result类型,并设置default="true",
指定该结果Result是默认的Result类型 -->
<result-type name="dispatcher"
class="org.apache.struts2.dispatcher.Servlet
DispatcherResult" default="true"/>
<!-- 定义FreeMarker的Result类型 -->
<result-type name="freemarker"
class="org.apache.struts2.views.freemarker.
FreemarkerResult"/>
...
</result-types>
<!-- 定义Struts 2内建的拦截器 -->
<interceptors>
<interceptor name="alias" class="com.opensymphony.xwork2.
interceptor.AliasInterceptor"/>
<interceptor name="autowiring"
class="com.opensymphony.xwork2.spring.interceptor.
ActionAutowiringInterceptor"/>
...
<!-- 定义基本拦截器栈 -->
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
<!-- 还有系列拦截器栈 -->
...
</interceptors>
<!-- 定义默认的拦截器栈引用 -->
<default-interceptor-ref name="defaultStack"/>
</package>
</struts>
上面的代码并未全部列出struts-default.xml文件,只是列出了每个元素的代表。上面配置文件中定义了一个名字为struts-default的包空间,该包空间里定义了Struts 2内建的Result类型,还定义了Struts 2内建的系列拦截器,以及由不同拦截器组成的拦截器栈,文件的最后还定义了默认的拦截器引用。
这个struts-default.xml文件是Struts 2框架的默认配置文件,Struts 2框架每次都会自动加载该文件。查看我们前面使用的struts.xml文件,看到我们自己定义的package元素有如下代码片段:
<!-- 指定Struts 2配置文件的根元素 -->
<struts>
<!-- 配置名为lee的包空间,继承struts-default包空间 -->
<package name="lee" extends="struts-default">
...
</package>
</struts>
在上面配置文件中,名为lee的包空间,继承了名为struts-default的包空间,struts-default包空间定义在struts-default.xml文件中。可见,Struts 2框架默认会加载struts-default.xml文件。
不仅如此,Struts 2框架提供了一种类似Eclipse的扩展方式,它允许以一种“可插拔”的方式来安装插件,例如后面将要介绍的Spring插件、JSF插件等,它们都提供了一个类似struts2-Xxx-plugin.jar的文件——这个文件就是插件安装文件,只要将该文件放在Web应用的WEB-INF/lib路径下,Struts 2框架将自动加载该框架。
使用WinRAR工具打开struts2-spring-plugin2.06.jar文件,找到一个struts-plugin.xml文件,打开该文件,该文件的代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 指定Struts 2的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 定义一个名字为spring的ObjectFactory -->
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring"
class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
<!-- 指定名字为sping的ObjectFactory是Struts 2的ObjectFactory -->
<constant name="struts.objectFactory" value="spring" />
<!-- 定义名为spring-default的包空间 -->
<package name="spring-default">
<!-- 定义整合Spring框架所必需的拦截器列表 -->
<interceptors>
<interceptor name="autowiring"
class="com.opensymphony.xwork2.spring.interceptor.
ActionAutowiringInterceptor"/>
<interceptor name="sessionAutowiring"
class="org.apache.struts2.spring.interceptor.
SessionContextAutowiringInterceptor"/>
</interceptors>
</package>
</struts>
在上面配置文件中,配置了Struts 2与Spring框架整合必需的常量、拦截器等——这也是Struts 2框架使用Spring插件必需的配置。只要将struts2-spring-plugin2.06.jar文件放在WEB-INF/lib路径下,Struts 2框架将自动加载该文件。
提示 如果用户开发属于自己的Struts 2插件,只要将对应的struts-plugin. xml文件放在JAR文件中,Struts 2框架将自动加载该文件。通过这种方式,Struts 2框架允许使用可插拔的方式管理Struts 2的插件。
3.5.3 struts.properties配置文件
Struts 2框架有两个核心配置文件,其中struts.xml文件主要负责管理应用中的Action映射,以及该Action包含的Result定义等。除此之外,Struts 2框架还包含一个struts.properties文件,该文件定义了Struts 2框架的大量属性,开发者可以通过改变这些属性来满足应用的需求。
struts.properties文件是一个标准的Properties文件,该文件包含了系列的key-value对象,每个key就是一个Struts 2属性,该key对应的value就是一个Struts 2属性值。
struts.properties文件通常放在Web应用的WEB-INF/classes路径下。实际上,只要将该文件放在Web应用的CLASSPATH路径下,Struts 2框架就可以加载该文件。
现在的问题是,struts.properties文件的哪些key是有效的?即struts.properties文件里包含哪些属性是有效的Struts 2属性。下面列出了可以在struts.properties中定义的Struts 2属性。
— struts.configuration:该属性指定加载Struts 2配置文件的配置文件管理器。该属性的默认值是org.apache.Struts2.config.DefaultConfiguration,这是Struts 2默认的配置文件管理器。如果需要实现自己的配置管理器,开发者则可以实现一个实现Configuration接口的类,该类可以自己加载Struts 2配置文件。
— struts.locale:指定Web应用的默认Locale。
— struts.i18n.encoding:指定Web应用的默认编码集。该属性对于处理中文请求参数非常有用,对于获取中文请求参数值,应该将该属性值设置为GBK或者GB2312。
提示 当设置该参数为GBK时,相当于调用HttpServletRequest的setCharacterEncoding方法。
— struts.objectFactory:指定Struts 2默认的ObjectFactory Bean,该属性默认值是spring。
— struts.objectFactory.spring.autoWire:指定Spring框架的自动装配模式,该属性的默认值是name,即默认根据Bean的name属性自动装配。
— struts.objectFactory.spring.useClassCache:该属性指定整合Spring框架时,是否缓存Bean实例,该属性只允许使用true和false两个属性值,它的默认值是true。通常不建议修改该属性值。
— struts.objectTypeDeterminer:该属性指定Struts 2的类型检测机制,通常支持tiger和notiger两个属性值。
— struts.multipart.parser:该属性指定处理multipart/form-data的MIME类型(文件上传)请求的框架,该属性支持cos、pell和jakarta等属性值,即分别对应使用cos的文件上传框架、pell上传及common-fileupload文件上传框架。该属性的默认值为jakarta。
注意 如果需要使用cos或者pell的文件上传方式,则应该将对应的JAR文件复制到Web应用中。例如,使用cos上传方式,则需要自己下载cos框架的JAR文件,并将该文件放在WEB-INF/lib路径下。
— struts.multipart.saveDir:该属性指定上传文件的临时保存路径,该属性的默认值是javax.servlet.context.tempdir。
— struts.multipart.maxSize:该属性指定Struts 2文件上传中整个请求内容允许的最大字节数。
— struts.custom.properties:该属性指定Struts 2应用加载用户自定义的属性文件,该自定义属性文件指定的属性不会覆盖struts.properties文件中指定的属性。如果需要加载多个自定义属性文件,多个自定义属性文件的文件名以英文逗号(,)隔开。
— struts.mapper.class:指定将HTTP请求映射到指定Action的映射器,Struts 2提供了默认的映射器:org.apache.struts2.dispatcher.mapper.DefaultActionMapper。默认映射器根据请求的前缀与Action的name属性完成映射。
— struts.action.extension:该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts 2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。
— struts.serve.static:该属性设置是否通过JAR文件提供静态内容服务,该属性只支持true和false属性值,该属性的默认属性值是true。
— struts.serve.static.browserCache:该属性设置浏览器是否缓存静态内容。当应用处于开发阶段时,我们希望每次请求都获得服务器的最新响应,则可设置该属性为false。
— struts.enable.DynamicMethodInvocation:该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。
— struts.enable.SlashesInActionNames:该属性设置Struts 2是否允许在Action名中使用斜线,该属性的默认值是false。如果开发者希望允许在Action名中使用斜线,则可设置该属性为true。
— struts.tag.altSyntax:该属性指定是否允许在Struts 2标签中使用表达式语法,因为通常都需要在标签中使用表达式语法,故此属性应该设置为true,该属性的默认值是true。
— struts.devMode:该属性设置Struts 2应用是否使用开发模式。如果设置该属性为true,则可以在应用出错时显示更多、更友好的出错提示。该属性只接受true和flase两个值,该属性的默认值是false。通常,应用在开发阶段,将该属性设置为true,当进入产品发布阶段后,则该属性设置为false。
— struts.i18n.reload:该属性设置是否每次HTTP请求到达时,系统都重新加载资源文件。该属性默认值是false。在开发阶段将该属性设置为true会更有利于开发,但在产品发布阶段应将该属性设置为false。
提示 开发阶段将该属性设置了true,将可以在每次请求时都重新加载国际化资源文件,从而可以让开发者看到实时开发效果;产品发布阶段应该将该属性设置为false,是为了提供响应性能,每次请求都需要重新加载资源文件会大大降低应用的性能。
— struts.ui.theme:该属性指定视图标签默认的视图主题,该属性的默认值是xhtml。
— struts.ui.templateDir:该属性指定视图主题所需要模板文件的位置,该属性的默认值是template,即默认加载template路径下的模板文件。
— struts.ui.templateSuffix:该属性指定模板文件的后缀,该属性的默认属性值是ftl。该属性还允许使用ftl、vm或jsp,分别对应FreeMarker、Velocity和JSP模板。
— struts.configuration.xml.reload:该属性设置当struts.xml文件改变后,系统是否自动重新加载该文件。该属性的默认值是false。
— struts.velocity.configfile:该属性指定Velocity框架所需的velocity.properties文件的位置。该属性的默认值为velocity.properties。
— struts.velocity.contexts:该属性指定Velocity框架的Context位置,如果该框架有多个Context,则多个Context之间以英文逗号(,)隔开。
— struts.velocity.toolboxlocation:该属性指定Velocity框架的toolbox的位置。
— struts.url.http.port:该属性指定Web应用所在的监听端口。该属性通常没有太大的用户,只是当Struts 2需要生成URL时(例如Url标签),该属性才提供Web应用的默认端口。
— struts.url.https.port:该属性类似于struts.url.http.port属性的作用,区别是该属性指定的是Web应用的加密服务端口。
— struts.url.includeParams:该属性指定Struts 2生成URL时是否包含请求参数。该属性接受none、get和all三个属性值,分别对应于不包含、仅包含GET类型请求参数和包含全部请求参数。
— struts.custom.i18n.resources:该属性指定Struts 2应用所需要的国际化资源文件,如果有多份国际化资源文件,则多个资源文件的文件名以英文逗号(,)隔开。
— struts.dispatcher.parametersWorkaround:对于某些Java EE服务器,不支持HttpServlet Request调用getParameterMap()方法,此时可以设置该属性值为true来解决该问题。该属性的默认值是false。对于WebLogic、Orion和OC4J服务器,通常应该设置该属性为true。
— struts.freemarker.manager.classname:该属性指定Struts 2使用的FreeMarker管理器。该属性的默认值是org.apache.struts2.views.freemarker.FreemarkerManager,这是Struts 2内建的FreeMarker管理器。
— struts.freemarker.wrapper.altMap:该属性只支持true和false两个属性值,默认值是true。通常无需修改该属性值。
— struts.xslt.nocache:该属性指定XSLT Result是否使用样式表缓存。当应用处于开发阶段时,该属性通常被设置为true;当应用处于产品使用阶段时,该属性通常被设置为false。
— struts.configuration.files:该属性指定Struts 2框架默认加载的配置文件,如果需要指定默认加载多个配置文件,则多个配置文件的文件名之间以英文逗号(,)隔开。该属性的默认值为struts-default.xml,struts-plugin.xml,struts.xml,看到该属性值,读者应该明白为什么Struts 2框架默认加载struts.xml文件了。
在有些时候,开发者不喜欢使用额外的struts.properties文件,Struts 2允许在struts.xml文件中管理Struts 2属性,在struts.xml文件中通过配置constant元素,一样可以配置这些属性。
struts.xml配置片段如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 指定Struts 2的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 通过constant元素配置Struts 2的属性 -->
<constant name=" struts.custom.i18n.resources " value="mess" />
...
</struts>
上面代码片段配置了一个常用属性:struts.custom.i18n.resources,该属性指定了应用所需的国际化资源文件的baseName为mess。
提示 Struts 2提供了两种方式来管理Struts 2的属性:既可以通过struts.properties文件来配置Struts 2属性,也可通过在struts.xml文件中配置constant元素来配置Struts 2属性。
3.5.4 struts.xml文件结构
struts.xml文件是整个Struts 2框架的核心,下面提供了一个最完整的struts.xml文件,这个文件没有任何实际意义,仅仅是一个struts.xml文件示范。
<?xml version="1.0" encoding="GBK"?>
<!-- 下面指定Struts 2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- struts是Struts 2配置文件的根元素 -->
<struts>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<constant name="" value="" />
<!-- 下面元素可以出现0次,也可以无限多次 -->
<bean type="" name="" class="" scope="" static="" optional="" />
<!-- 下面元素可以出现0次,也可以无限多次 -->
<include file="" />
<!-- package元素是Struts配置文件的核心,该元素可以出现0次,或者无限多次 -->
<package name="必填的包名" extends="" namespace="" abstract=""
externalReferenceResolver>
<!-- 该元素可以出现,也可以不出现,最多出现一次 -->
<result-types>
<!-- 该元素必须出现,可以出现无限多次-->
<result-type name="" class="" default="true|false">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result-type>
</result-types>
<!-- 该元素可以出现,也可以不出现,最多出现一次 -->
<interceptors>
<!-- 该元素的interceptor元素和interceptor-stack至少出现其中之一,
也可以二者都出现 -->
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor name="" class="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor-stack name="">
<!-- 该元素必须出现,可以出现无限多次-->
<interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<default-interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>
</default-interceptor-ref>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<default-action-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</default-action-ref>?
<!-- 下面元素可以出现0次,也可以无限多次 -->
<global-results>
<!-- 该元素必须出现,可以出现无限多次-->
<result name="" type="">
<!-- 该字符串内容可以出现0次或多次 -->
映射资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result>
</global-results>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<global-exception-mappings>
<!-- 该元素必须出现,可以出现无限多次-->
<exception-mapping name="" exception="" result="">
异常处理资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</exception-mapping>
</global-exception-mappings>
<action name="" class="" method="" converter="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
<!-- 下面元素可以出现0次,也可以无限多次 -->
<result name="" type="">
映射资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor-ref>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<exception-mapping name="" exception="" result="">
异常处理资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</exception-mapping>
</action>
</package>*
<struts>
上面的struts.xml配置文件是一个非常全面的配置文件,包含了Struts 2的全部配置元素。上面配置文件主要用于帮助对XML DTD不太熟悉的读者。
实际上,如果读者需要熟悉XML文件的DTD(元素类型定义)语法,通过查看Struts 2配置文件的DTD信息,同样可以了解Struts 2配置文件的结构。下面是Struts 2配置文件的DTD文件代码。
<!--
Struts configuration DTD.
Use the following DOCTYPE
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
-->
<!ELEMENT struts (package|include|bean|constant)*>
<!ELEMENT package (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, global-results?, global-exception-mappings?, action*)>
<!ATTLIST package
name CDATA #REQUIRED
extends CDATA #IMPLIED
namespace CDATA #IMPLIED
abstract CDATA #IMPLIED
externalReferenceResolver NMTOKEN #IMPLIED
>
<!ELEMENT result-types (result-type+)>
<!ELEMENT result-type (param*)>
<!ATTLIST result-type
name CDATA #REQUIRED
class CDATA #REQUIRED
default (true|false) "false"
>
<!ELEMENT interceptors (interceptor|interceptor-stack)+>
<!ELEMENT interceptor (param*)>
<!ATTLIST interceptor
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ELEMENT interceptor-stack (interceptor-ref+)>
<!ATTLIST interceptor-stack
name CDATA #REQUIRED
>
<!ELEMENT interceptor-ref (param*)>
<!ATTLIST interceptor-ref
name CDATA #REQUIRED
>
<!ELEMENT default-interceptor-ref (param*)>
<!ATTLIST default-interceptor-ref
name CDATA #REQUIRED
>
<!ELEMENT default-action-ref (param*)>
<!ATTLIST default-action-ref
name CDATA #REQUIRED
>
<!ELEMENT global-results (result+)>
<!ELEMENT global-exception-mappings (exception-mapping+)>
<!ELEMENT action (param|result|interceptor-ref|exception-mapping)*>
<!ATTLIST action
name CDATA #REQUIRED
class CDATA #IMPLIED
method CDATA #IMPLIED
converter CDATA #IMPLIED
>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param
name CDATA #REQUIRED
>
<!ELEMENT result (#PCDATA|param)*>
<!ATTLIST result
name CDATA #IMPLIED
type CDATA #IMPLIED
>
<!ELEMENT exception-mapping (#PCDATA|param)*>
<!ATTLIST exception-mapping
name CDATA #IMPLIED
exception CDATA #REQUIRED
result CDATA #REQUIRED
>
<!ELEMENT include (#PCDATA)>
<!ATTLIST include
file CDATA #REQUIRED
>
<!ELEMENT bean (#PCDATA)>
<!ATTLIST bean
type CDATA #IMPLIED
name CDATA #IMPLIED
class CDATA #REQUIRED
scope CDATA #IMPLIED
static CDATA #IMPLIED
optional CDATA #IMPLIED
>
<!ELEMENT constant (#PCDATA)>
<!ATTLIST constant
name CDATA #REQUIRED
value CDATA #REQUIRED
>
通过上面给出的struts.xml配置文件范例,以及Struts 2配置文件的DTD信息,我们已经可以全面掌握Struts 2配置文件的文件结构了,但struts.xml文件各配置元素的意义、各属性的相关含义依然不能深入了解。关于Struts 2配置文件各元素及各属性将在下一章深入介绍。
3.6 本章小结
因为Struts 2是以Struts 1和WebWork为基础的,故本章大致介绍了Struts 1和WebWork框架的MVC实现,并在此基础上侧重于介绍Struts 1和WebWork与Struts 2的延续性,以及Struts 2框架与Struts 1和WebWork的类似性。通过阅读本章,读者应该明白:Struts 2是通过WebWork发展起来的,而不是通过Struts 1发展起来的。
本章还介绍了如何通过Eclipse IDE工具来开发Struts 2应用,Struts 2的配置文件,以及通过web.xml文件加载Struts 2框架。详细介绍了struts.properties文件,以及struts.xml配置文件的结构。关于深入配置struts.xml文件的元素,则放在下一章介绍