`
一眼泉水
  • 浏览: 517303 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论
阅读更多
WebWork教程-0.90版
作者:moxie(安子)
发布记录:
2004-6-29:WebWork教程0.90 版
说明:本次发布的是初稿部分,除了章节:“实战G-Roller-WW”、“WebWork与其它开源项
目的集成”,其余章节全部完成。
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
目 录
WebWork教程-0.90 版.................................................................................................................1
WebWork介绍.........................................................................................................................3
WebWork安装-HelloWorld...................................................................................................3
WebWork安装.................................................................................................................3
HelloWorld.......................................................................................................................4
Action(动作).............................................................................................................................6
Action介绍.......................................................................................................................6
用户注册例子...................................................................................................................6
Field-Driven Action vs. Model-Driven Action ...............................................................10
ActionContext(Action上下文) ..............................................................................................12
ActionContext介绍........................................................................................................12
ServletActionContext......................................................................................................13
ServletDispatcher 原理...........................................................................................................14
Action的单元测试.........................................................................................................17
Result Type .............................................................................................................................18
表达式与言EL和OGNL......................................................................................................22
OGNL介绍....................................................................................................................22
值堆栈-OgnlValueStack...............................................................................................22
Interceptor(拦截器)框架........................................................................................................28
Interceptor 的原理..........................................................................................................31
验证框架...............................................................................................................................32
为用户注册添加验证功能.............................................................................................32
XWork配置详述....................................................................................................................37
xwork.xml 文件的标签元素..........................................................................................37
实战G-Roller-WW................................................................................................................41
G-Roller-WW介绍.........................................................................................................41
JSTL与WebWork的整合.............................................................................................41
中文解决方案.................................................................................................................41
WebWork与其它开源项目的集成........................................................................................41
Spring.............................................................................................................................41
Hibernate........................................................................................................................41
Xml-RPC ........................................................................................................................41
总结.......................................................................................................................................42
附录.......................................................................................................................................42
我钟爱的Opensympnony ..............................................................................................42
从技术的角度Struts1.1 与WebWork2 的比较.............................................................44
WebWork的项目资源....................................................................................................46
参考资料........................................................................................................................46
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
WebWork介绍
WebWork是由OpenSymphony 组织开发的,致力于组件化和代码重用的拉出式MVC模式
J2EE Web 框架。WebWork目前最新版本是2.1,现在的WebWork2.x 前身是Rickard Oberg
开发的WebWork,但现在WebWork已经被拆分成了Xwork1 和WebWork2 两个项目,如下
示意图所示:
Xwork简洁、灵活功能强大,它是一个标准的Command模式实现,并且完全从web 层
脱离出来。Xwork提供了很多核心功能:前端拦截机(interceptor),运行时表单属性验证,
类型转换,强大的表达式语言(OGNL – the Object Graph Notation Language),IoC(Inversion
of Control 倒置控制)容器等。
WebWork2 建立在Xwork 之上,处理HTTP 的响应和请求。WebWork2 使用
ServletDispatcher 将HTTP 请求的变成Action(业务层Action 类), session(会话)application
(应用程序)范围的映射,request请求参数映射。WebWork2 支持多视图表示,视图部分可
以使用JSP, Velocity, FreeMarker, JasperReports,XML 等。
下面我们提到的WebWork将为WebWork2,使用的版本是2.1。
HelloWorld实例
首先看下面这个程序HelloWorldAction.java:
package helloWorld
import com.opensymphony.xwork.Action;
public class HelloWorldAction implements Action{
String greeting;
public String getGreeting() {
return greeting;
}
public String execute() throws Exception {
greeting = "Hello World!";
return SUCCESS;
}
}
HelloWorldAction 是一个普通的Java 类,它实现了Action 这个接口。Action 是一个非
常简单的接口,只有一个方法:public String execute() throws Exception; ,Action类介绍见
下一节。HelloWorldAction有一个String类型字段greeting,在execute()方法中,greeting被
赋值“Hello World!”,并返回String型常量SUCCESS,SUCCESS的定义详见Action接口,
这个常量代表了execute()方法执行成功,将返回成功页面。
返回的页面greetings.jsp代码如下:
<%@ taglib prefix="ww" uri="webwork" %>
<html>
<head>
<title>First WebWork Example</title>
</head>
<body>
<p><ww:property value="greeting"/></p>
</body>
</html>
greetings.jsp很简单的jsp页面,它使用了WebWork自带的标签库。它的作用是输出变
量“greeting”的值。这个<ww:property value="greeting"/>语句,相当于调用相应Action
(HelloWorldAction)的getGreeting()方法,取得变量“greeting”的值。
我们的HelloWorld代码就这么多,完了。可是,HelloWorldAction怎么去调用、执行?
执行成功它又怎么知道返回到greetings.jsp?XWork 的配置文件xwork.xml 会负责将要执行
的Action和展现的视图连接起来,见xwork.xml 的如下片断:
<action name="hello" class=" helloWorld .HelloWorldAction">
<result name="success" type="dispatcher">
<param name="location">/greetings.jsp</param>
</result>
</action>
我们先看action标签:name=”hello”,表示我们调用这个Action的标识是hello,这样我
们可以通过下面的url访问这个Action:…/hello.action,
例如:http://localhost:8080/webwork/hello.action;class=" helloWorld .HelloWorldAction"很好
理解,这是真正调用执行的类。我们在看看result 标签:name="success",记得前面
HelloWorldAction返回的字符常量SUCCESS吗?它的值其实就是“success”,它表示Action
执行成功返回success就转向这个结果;type="dispatcher"表示执行完Action,转向结果页面
的方式;param参数指定了结果页面的位置:/greetings.jsp。
代码写完,剩下的当然是编译、部署。启动tomcat服务器之后我们就可以执行了:
在浏览器里输入你的地址:http://localhost:8080/webwork/hello.action
你将会看到结果。
Action(动作)
Action介绍
Action 在MVC 模式中担任控制部分的角色,在WebWork 中使用的最多。每个请求的
动作都对应于一个相应的Action,一个Action 是一个独立的工作单元和控制命令,它必需
要实现XWork里的Action接口,实现Action接口的execute()方法。Action接口的代码如下:
package com.opensymphony.xwork;
import java.io.Serializable;
public interface Action extends Serializable {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}
excute()方法是Action类里最重要的部分,它执行返回String类型的值,在Action中返
回的值一般使用它上面定义的标准静态字符常量。例如:前面的HelloWorldAction返回的就
是SUCCESS 字符常量,真正的值当然就是“success”,它与xwork 配置文件里result 标签
name的值是相对应的。它用来决定execute()方法执行完成之后,调用哪一种返回结果。
字符常量的含义如下:
SUCCESS:Action正确的执行完成,返回相应的视图;
NONE:表示Action正确的执行完成,但并不返回任何视图;
ERROR:表示Action执行失败,返回到错误处理视图;
INPUT:Action 的执行,需要从前端界面获取参数,INPUT 就是代表这个参数输入的
界面,一般在应用中,会对这些参数进行验证,如果验证没有通过,将自动返回到该视图;
LOGIN:Action 因为用户没有登陆的原因没有正确执行,将返回该登陆视图,要求用
户进行登陆验证。
用户注册例子
下面我们将以一个用户注册的例子详细介绍Action的原理:
功能描述:一个用户注册页面register.jsp,用户可以在这个页面里输入用户注册的基本
信息(例如:姓名、密码、Email 等),输入完成提交表单,执行用户注册的Action,执行
成功返回成功提示的页面(register-result.jsp)并将注册的信息输出。
模型:User.java
控制:RegisterAction.java
视图:register.jsp、register-result.jsp
配置:xwork.xml
User.java:
package example.register;

public class User {
private String username;
private String password;
private String email;
private int age;

public void setUsername(String username) {
this.username = username;
}

public String getUsername() {
return username;
}

public void setPassword(String password) {
this.password = password;
}

public String getPassword() {
return password;
}

public void setEmail(String email) {
this.email = email;
}

public String getEmail() {
return email;
}

public void setAge(int age) {
this.age = age;
}

public int getAge() {
return age;
}

public String toString() {
return "username=" + username + ",password=" + password + ",email="
+ email + ",age=" + age;
}
}
模型User 是一个普通的JavaBean,它包含了用户注册的字段信息,并对每个字段提供
相应的set和get方法。下面我们来看看进行用户注册动作的RegisterAction.java:
package example.register;
import com.opensymphony.xwork.Action;
public class RegisterAction implements Action{
private User user=new User();
public User getUser()
{
return user;
}
public String execute()
{
System.out.println("开始注册");
System.out.println("User="+user);
return SUCCESS;
}

}这个Action 是不是特清爽?用户注册就这么几行代码搞定,当然,我们提倡在Action
里最好不要实现业务代码,Action的主要功能是提供从请求中取得参数的值,转化成相应的
模型,再将模型传递给执行业务操作的对象,比如:将注册的用户信息存储到数据库中,由
业务对象执行业务操作,再返回执行的结果。为了简化我们省去了注册的业务逻辑执行步骤。
再看看我们注册信息输入的页面:register.jsp
<html>
<head><title>Register Example</title></head>
<body>
<table border=0 width=97%>
<tr><td align="left">
<form name="register" action="register.action" method="post">
Username:<input type="text" name="user.username"><br>
Password:<input type="text" name="user.password"><br>
Email:<input type="text" name="user.email"><br>
Age:<input type="text" name="user.age"><br>
<input type="submit" name="Submit"><br>
</form>
</td></tr>
</table>
</body>
</html>
register.jsp页面其实只是一个普通的HTML页面,它提供了一个表单,用来接受用户输
入的注册信息,它唯一特殊的部分就是input 输入框定义的name 部分,例如:用户姓名用
的是“user. username”。这种命名方式代表什么含义?它是必需的吗?后面我们将会给出答
案。
RegisterAction正确执行完成之后,会将执行的结果返回到register-result.jsp页面,由它
来显示用户在前面页面输入的注册信息。register-result.jsp代码如下:
<%@ taglib prefix="ww" uri="webwork" %>
<html>
<head><title>Register result</title></head>
<body>
<table border=0 width=97%>
<tr>
<td align="left">
Congratulation,your register success!<p>
Username:<ww:property value="user.username"/><br>
Password:<ww:property value="user.password"/><br>
Email:<ww:property value="user.email"/><br>
Age:<ww:property value="user.age"/><br>
</td>
</tr>
</table>
</body>
</html>
这个Jsp 页面使用了WebWork 的标签库<ww:property />,记得HelloWorld 里的
greetings.jsp吗?它也使用了这个标签库。我们看这个:<ww:property value="user.username"/>
它是一个普通的使用标签库语句,查看这个标签库的源程序,见包
com.opensymphony.webwork.views.jsp 里的PropertyTag.java 文件,你会发现这个类会根据
value 后面赋予的表达式值,去OgnlValueStack 里查找这个表达式值所对应的操作。执行这
个语句OgnlValueStack 会根据value 的值(一个表达式)“user.username”去分别调用
RegisterAction类的getUser()和User 类的getUsername()方法,即:getUser().getUsername(),
取得的数据就是前面注册页面输入的用户名。
我们把“user.username”这样的语句叫做表达式语言(Expression Language,简称为EL)。
它由XWork框架提供,XWork表达式语言的核心是OGNL(Object Graph Notation Language),
OGNL是一种功能强大,技术成熟,应用广泛的表达式语言,将在下面的章节有详细介绍。
我们在回到前面介绍的register.jsp,Input输入框<input type="text" name="user.username">里用的“user.username”,现在我们可以明白,它不是随意设置的,它是一个表达式语言,有着特殊的功能。看到这里,不知道你心中是否有一个疑问:我们的RegisterAction是如何取得用户注册页面输入的数据呢?如果你做过Web 开发,你一定会想到RegisterAction里必需有一些从客户端请求中获取参数的语句,例如: 类似: String username = request.getParameter(“user. username”) 的语句( request 是
HttpServletRequest 的对象),去从request 请求里面获取用户输入的参数值。可是我们这个
Action 里面只有User 对象简单的get 方法,并没有其它的代码。Xwork 框架的Action 是如
何去实现了与Web 无关?request请求的参数是怎么传递到我们Action的模型User 中呢?
在回答答案之前,我们先看一看Xwork的配置文件xwork.xml:
<action name="register" class="example.register.RegisterAction" >
<result name="success" type="dispatcher">
<param name="location">/register-result.jsp</param>
</result>
<interceptor-ref name="params"/>
</action>
看了前面的介绍,这段配置文件应该不难理解。用户通过注册页面register.jsp输入自己
的注册信息,提交表单到动作register.action,它将有ServletDispatcher 调度,从配置文件
xwork.xml 里查找与“register”匹配的Action 名字,即上面配置的Action。通过这个名字
XWork 框架找到这个Action 的类:example.register.RegisterAction,XWork 框架会负责去创建这个Action 类的对象并调用execute()方法进行用户注册操作。正确执行execute()方法返
回String类型数据“success”之后,它会请求再派遣到register-result.jsp页面。
在这段配置文件里,你一定注意到了它特殊的一句:<interceptor-ref name="params"/>,
interceptor-ref标签设置这个Action用到的拦截器(Interceptor),“params”引用的是配置文件
<interceptorname="params"class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>,这个拦截器将在RegisterAction的execute()方法执行之前调用,作用是将request 请求的参数值通过表达式语言设置到相应RegisterAction的模型里。例如:register.jsp里的<input type="text" name="user.username">,它输入的值会由RegisterAction类的getUser()和User类的setUserName(“…”)设置到这个User模型里。假设你在注册页面输入用户名“moxie”,提交表单ParametersInterceptor 就会下面的操作:首先从请求中取得参数的名字和名字对应的值,分别为:“user.username”和“moxie”,根据这个名字,从OgnlValueStack中取得堆栈最上面的getUser().setUsername(“moxie”)操作,即取得RegisterAction对象的User 模型,并设置username属性的值为“moxie”。原来,我们的Action是通过XWork的拦截器ParametersInterceptor 从提交的表单中取得请求的参数和值,再通过OgnlValueStack来执行表达式,调用Action和模型里相应的ge或set 方法,将从请求中取得的值设置到模型中去。register.jsp 中Input 输入框的name="user.username"是必需要遵守OGNL 的命名规则。也正是很多拦截器的使用,使得我们的Action类和Web 实现了完全的解耦,让我们的Action能如此的简单、优雅,拦截器的原理后面章节我们也将会有详细的介绍。
罗索了这么多,你一定是精通了这个用户注册的例子了吧!呵呵!
Field-Driven Action vs. Model-Driven Action
Action根据FormBean的不同可以分为二类,
一类是 Field-Driven(字段驱动的)Action
Action 将直接用自己的字段来充当FormBean 的功能,我们的例子就是使用这种方式。
它一般用在页面表单比较简单的情况使用,而且可以直接用域对象作为Action 的字段,这
样就不用在另写FormBean,减少了重复代码。
另一类是 Model-Driven(模型驱动的)Action
它很像Struts 的FormBean,但在WebWork 中,只要普通Java 对象就可以充当模型部
分。Model-Driven(模型驱动的)Action 要求我们的Action 实现com.opensymphony.xwork.
ModelDriven接口,它有一个方法:Object getModel();,我们用这个方法返回我们的模型对
象就可以了。我们可以将前面的RegisterAction.java 改为Model-Driven(模型驱动的)Action:
package example.register;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ModelDriven;
/**
* @author moxie-qac
* achqian@yahoo.com.cn
*
*/
public class RegisterActionModel implements Action,ModelDriven{
private User user = new User();
public String execute() throws Exception {
System.out.println("Start execute......。。。。。。。。。。。。。。
");
System.out.println("User="+user);
//在这里调用用户注册的业务逻辑,比如:将注册信息存储到数据库
return SUCCESS;
}
public Object getModel() {
return user;
}
}
这时我们输入信息的页面也有了变化:register-model.jsp
<%@page language="java" contentType="text/html; charset=GBK"%>
<html>
<head>

<title>My JSP 'register-model.jsp' starting page</title>

</head>

<body>
<form action="register.action" method="post">
<table border="0" width="100" align="center">
<tr>
<th colspan=2>
<div>
注册信息
</div>
</th>
</tr>
<tr>
<td>
<div>
Username:
</div>
</td>
<td>
<input type="text" name="username">
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
<input type="text" name="password">
</td>
</tr>
<tr>
<td>
Email:
</td>
<td>
<input type="text" name="email">
</td>
</tr>
<tr>
<td>
Age:
</td>
<td>
<input type="text" name="age">
</td>
</tr>
<th colspan="2">
<input type="submit">
</th>
<tr>
</tr>
</table>
</form>
</body>
</html>
我们发现,输入框里的命名发生了变化。它们都少了“user.”这部分信息。
当我们采用Model-Driven(模型驱动的)Action 时,它将取得模型对象保存在值堆栈中。
“name="username"”就是代表直接调用模型对象的setUsername()方法。
同时,输出信息的界面也要变化,把user.username等都换成username.具体如下:
Register-result.jsp
<%@ page language="java" contentType="text/html;charset=GBK"
pageEncoding="GBK"%>
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<head>
<title>Register Result</title>
</head>
<body>
<table border="0" width="100" align="center">
<tr>
<th colspan="2">
<div align="center">
注册信息
</div>
</th>
</tr>
<tr>
<td>
Username:
</td>
<td>
<ww:property value="username" />
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
<ww:property value="password" />
</td>
</tr>
<tr>
<td>
Email:
</td>
<td>
<ww:property value="email" />
</td>
</tr>
<tr>
<td>
Age:
</td>
<td>
<ww:property value="age" />
</td>
</tr>
</table>
</body>
</html>
我们Action 的在配置文件中,也要给它指定一个拦截器model-driven,它的作用就是将
模型对象保存到值堆栈中。关于拦截器的介绍请看下面的章节。
配置文件如下:xwork.xml
<action name="registerModel"
class="example.register.RegisterActionModel">
<result name="success" type="dispatcher">
<param name="location">/register-result-model.jsp</param>
</result>
<interceptor-ref name="model-driven"/>
<interceptor-ref name="params"/>
</action>
ActionContext(Action 上下文)
ActionContext介绍
通过上面用户注册例子的学习,我们知道Xwork与Web 无关性,我们的Action不用去
依赖于任何Web 容器,不用和那些JavaServlet复杂的请求(Request)、响应(Response)关联
在一起。对请求(Request)的参数(Param),可以使用拦截器框架自动调用一些get()和set()
方法设置到对应的Action 的字段中。但是,仅仅取得请求参数的值就能完全满足我们的功
能要求吗?不,在Web 应用程序开发中,除了将请求参数自动设置到Action的字段中,我
们往往也需要在Action 里直接获取请求(Request)或会话(Session)的一些信息,甚至需要
直接对JavaServlet Http的请求(HttpServletRequest)、响应(HttpServletResponse)操作。
带着这些问题,我们来看看下面的一个功能需求:
我们需要在Action中取得request请求参数“username”的值:
ActionContext context = ActionContext.getContext();
Map params = context.getParameters();
String username = (String) params.get(“username”);
为了实现这个功能,我们用了三个步骤:
1、取得我们当前的ActionContext对象context,ActionContext是个什么冬冬?
2、从 context对象里获取我们所有的请求参数,取得的却是一个Map对象params?
3、居然可以从我们的Map 对象params 里获取我们需要的request 请求参数
“username”的值。
ActionContext(com.opensymphony.xwork.ActionContext)是Action 执行时的上下文,
上下文可以看作是一个容器(其实我们这里的容器就是一个Map而已),它存放的是Action
在执行时需要用到的对象,比如:在使用WebWork 时,我们的上下文放有请求的参数
(Parameter)、会话(Session)、Servlet上下文(ServletContext)、本地化(Locale)信息等。
在每次执行Action 之前都会创建新的ActionContext,ActionContext 是线程安全的,也就
是说在同一个线程里ActionContext里的属性是唯一的,这样我的Action就可以在多线程中
使用。
我们可以通过ActionContext 的静态方法:ActionContext.getContext()来取得当前的
ActionContext对象,我们看看这段代码:
public static ActionContext getContext() {
ActionContext context = (ActionContext) actionContext.get();
if (context == null) {
OgnlValueStack vs = new OgnlValueStack();
context = new ActionContext(vs.getContext());
setContext(context);
}
return context;
}
一般情况,我们的ActionContext 都是通过:ActionContext context = (ActionContext)
actionContext.get();来获取的。我们再来看看这里的actionContext 对象的创建:static
ThreadLocal actionContext = new ActionContextThreadLocal();,ActionContextThreadLocal是
实现ThreadLocal 的一个内部类。ThreadLocal 可以命名为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。这样,我们ActionContext 里的属性只会在对应的当前请求线程中可见,从而保证它是线程安全的。
下面我们看看怎么通过ActionContext取得我们的HttpSession:
Map session = ActionContext.getContext().getSession();
原来我们取得的session 却是Map 类型的对象,这是为什么?原来,我们的WebWork
框架将与Web 相关的很多对象重新进行了包装,比如这里就将HttpSession对象重新包装成
了一个Map对象,供我们的Action使用,而不用直接和底层的HttpSession打交道。也正是
框架的包装,让我们的Actoion可以完全的和Web 层解藕。
如果我们的Action需要直接与JavaServlet 的HttpSession、HttpServletRequest等一些对
象进行操作,我们又该如何处理?请看下面的ServletActionContext。
ServletActionContext
ServletActionContext(com.opensymphony.webwork. ServletActionContext),这个类直接
继承了我们上面介绍的ActionContext,它提供了直接与JavaServlet 相关对象访问的功能,
它可以取得的对象有:
1、javax.servlet.http.HttpServletRequest:HTTPservlet请求对象
2、javax.servlet.http.HttpServletResponse;:HTTPservlet相应对象
3、javax.servlet.ServletContext:Servlet 上下文信息
4、javax.servlet.ServletConfig:Servlet配置对象
5、javax.servlet.jsp.PageContext:Http页面上下文
ServletActionContext 除了提供了上面这些对象访问,它当然也继承了它父类
ActionContex 的很多功能,比如:对OgnlValueStack、Action名字等的访问。
下面我们看看几个简单的例子,让我们了解如何从ServletActionContext 里取得
JavaServlet的相关对象:
1、取得 HttpServletRequest对象:
HttpServletRequest request = ServletActionContext. getRequest();
2、取得 HttpSession对象:
HttpSession session = ServletActionContext. getRequest().getSession();
ServletActionContext和ActionContext有着一些重复的功能,在我们的Action中,该如
何去抉择呢?我们遵循的原则是:如果ActionContext 能够实现我们的功能,那最好就不要
使用ServletActionContext,让我们的Action 尽量不要直接去访问JavaServlet 的相关对象。
在使用ActionContext 时有一点要注意:不要在Action 的构造函数里使用
ActionContext.getContext(),因为这个时候ActionContext里的一些值也许没有设置,这时通
过ActionContext取得的值也许是null。
ServletDispatcher原理
ServletDispatcher 是默认的处理Web Http 请求的调度器,它是一个JavaServlet,是
WebWork 框架的控制器。所有对Action 调用的请求都将通过这个ServletDispatcher 调度。
它将在web.xml 里配置ServletDispatcher 时指定,让所有对WebWork 的Action(默认的
是.action的后缀)的请求都对应到该调度的JavaServlet 中,具体配置在前面的WebWork安装
中有介绍。
ServletDispatcher 接受客户端__________的HTTP请求,将JavaServlet的很多相关对象进行包装,再传给我们的XWork框架,由我们的XWork框架去解析我们的xwork.xml配置文件,根据配置文件的信息,创建对应的Action,组装并调用相应的拦截器,执行Action,返回执行结果。WebWork使用XWork的核心,主要是由这个ServletDispatcher 去实现的,
ServletDispatcher 的主要功能调用如下:
一、init()方法在服务器启动时调用,
1、初始化Velocity引擎
2、检查是否支持配置文件重新载入功能。如果webwork.configuration.xml.reload(见
webwork.properties 文件)设置为true,每个request请求都将重新装载xwork.xml配置文件。
在开发环境使用将会非常方便,但在生产环境必需设置为false。
代码如下:
if ("true".equalsIgnoreCase(Configuration.getString("webwork.configuration.xml.reload"))) {
FileManager.setReloadingConfigs(true);
}
3、设置一些文件上传的信息,比如:上传临时目录,上传的最大字节等。都设置在
webwork.properties 文件里,如果在classpath 中找不到这个属性文件,它会去读取默认的
default.properties
二、service()方法,每次客户端的请求都将调用此方法。
1、通过request 请求取得action 的命名空间(namespace,与xwork.xml 配置文件里
package标签的name对应)
例如:/foo/bar/MyAction.action,取得的命名空间为/foo/bar
在xwork.xml配置文件里应该有这一段:
<package name="foo.bar" …….
2、根据servlet请求的Path,解析出要调用该请求的Action的名字(actionName),例
如:(../foo/bar/MyAction.action -> MyAction)
在 xwork.xml配置文件里应该有:
<package name="foo.bar" …….
<Action name=” MyAction”……
3、创建Action上下文(extraContext)。我们前面介绍的ActionContext上下文的对象,
就是在这里设置的。它将JavaServlet 相关的对象进行包装,放入到extraContext 这
个Map对象里。
/**
* 将所有的应用请求和servlet属性保存到一个HashMap中,
* @param requestMap 存放所有request请求属性的Map
* @param parameterMap 存放所有request请求参数的Map
* @param sessionMap存放所有session属性的Map
* @param applicationMap 存放所有servlet上下文属性的Map
* @param request HttpServletRequest 对象
* @param response HttpServletResponse 对象.
* @param servletConfig ServletConfig 对象.
* @return代表Action 上下文的一个HashMap
*/
public static HashMap createContextMap(Map requestMap, Map parameterMap,
Map sessionMap, Map applicationMap, HttpServletRequest request,
HttpServletResponse response, ServletConfig servletConfig) {
HashMap extraContext = new HashMap();
extraContext.put(ActionContext.PARAMETERS, parameterMap);
extraContext.put(ActionContext.SESSION, sessionMap);
extraContext.put(ActionContext.APPLICATION, applicationMap);
extraContext.put(ActionContext.LOCALE, request.getLocale());
extraContext.put(HTTP_REQUEST, request);
extraContext.put(HTTP_RESPONSE, response);
extraContext.put(SERVLET_CONFIG, servletConfig);
extraContext.put(COMPONENT_MANAGER,
request.getAttribute("DefaultComponentManager"));
// helpers to get access to request/session/application scope
extraContext.put("request", requestMap);
extraContext.put("session", sessionMap);
extraContext.put("application", applicationMap);
extraContext.put("parameters", parameterMap);
AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
return extraContext;
}
下面我们来看看它是如何将request请求的参数和session进行包装的:
protected Map getParameterMap(HttpServletRequest request) throws IOException {
return request.getParameterMap();
}
这个方法比较简单,它直接调用了HttpServletRequest 的方法getParameterMap(),将
所有request请求的参数封装到一个Map中。
protected Map getSessionMap(HttpServletRequest request) {
return new SessionMap(request);
}
这个方法取得所有Session 中的属性,它调用了com.opensymphony.webwork.dispatcher.
SessionMap类,这个类实现了Map接口,在entrySet()方法中列举Session的所有属性,
存放在Set中。
4、根据前面获得的namespace、actionName、extraContext,创建一个ActonProxy
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace,
actionName, extraContext);
默认的proxy 是com.opensymphony.xwork.DefaultActionProxy,在它的构造函数会进行
下面的操作:1)、根据namespace、actionName 读取xwork.xml配置文件里这个Action的所
有配置信息。
2)、创建ActionInvocation
invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext);
默认的invocation是com.opensymphony.xwork.DefaultActionInvocation,它的构造函数操
作有:
a) 由com.opensymphony.xwork.ObjectFactory创建我们配置文件描述的Action对
象。再将这个Action 对象存放入OgnlValueStack 中。记得我们前面用户注册
的例子吗?当用户提交表达时它会有表达式语言向OgnlValueStack 取得
Action 对象的字段,再把输入框的数据设置到对应的Action 字段中,这个
Action对象就是在这个时候进栈的。
b) 传入extraContext 参数,创建与ActionInvocation 对应的Action 上下文
(ActionContext)。记得我们在介绍ActionContext的最后,提出了一个需要注
意的地方:不要在Action构造函数中调用ActionContext.getContext()。现在应
该能明白,原来是Action对象实例在ActionContext 对象实例之前创建的,所
有这样取得ActionContext容器对象就有可能会返回null
c) 取得这个Action对应的所有拦截器(Interceptor),存放入java.util.Iterator 对象
中。
5、执行proxy的execute()方法,这个方法最核心的语句是:retCode = invocation.invoke();,
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
invocation对象的invoke()方法它遍历并执行这个Action对应的所有拦截器,执行Action对
应的方法(默认的是execute()),根据Action 执行返回的值去调用执行相应的Result(返回
结果处理)的方法。
Action的单元测试
理解了ServletDispatcher,我们就明白了整个框架调用执行的顺序。Action 虽然是与Web
无关,可是它的创建、参数设置、执行与我们的WebWork、XWork 紧密关联在一起,有我
们的控制器ServletDispatcher 去统一调度,那我们如何去对Action进行独立的单元测试呢?
请看下面的例子:使用单元测试框架JUnit对register.User. RegisterAction做单元测试
见 example.register. RegisterActionTest类testExecuteWithProxyFactory()方法:
public void testExecuteWithProxyFactory() throws Exception{
Map params = new HashMap();
params.put("user.username","Moxie");
params.put("user.password","mypassword");
params.put("user.email","achqian@yahoo.com.cn");
params.put("user.age",new Integer(23));
Map extraContext = new HashMap();
extraContext.put(ActionContext.PARAMETERS,params);
ActionProxy proxy =
ActionProxyFactory.getFactory().createActionProxy("example",
"register", extraContext);
proxy.setExecuteResult(false);
assertEquals(proxy.execute(),"success");
RegisterAction action = (RegisterAction) proxy.getAction();
assertEquals(action.getUser().getUsername(),"Moxie");
assertEquals(action.getUser().getAge(),23);
}
下面解说这个方法:
1、对象params 表示请求参数的Map,在它里面设置了注册用户的信息。extraContext当然就
是我们ActionContext上下文的容器,它里面保存了放置请求参数的对象params
2、创建我们的ActionProxy,它传入的参数有:“example”-这个Action 的命名空间,
“register”-Action 对应的名字,extraContext-存放Actin 上下文里的对象,,执行并
将它返回的值与“success”比较,测试Action 是否能正确执行完成。注意:
proxy.setExecuteResult(false);,因为我们是单元测试,所以Action 执行完成就可以了,
不用再去调用结果响应的操作,故将是否执行结果设置为“false”。
3、Action正确执行完成之后,我们也可以测试现在Action的字段里的数据是否按照我们预
期的要求正确设置。从ActionProxy对象里取得执行的Action,即RegisterAction对象,
再取得它的User 模型,将其数据与前面设置参数的数据进行比较,判断它是否等于我
Result Type
前面我们学习了ServletDispatcher,它是WebWork 框架机制的核心。它和Action 在我
们MVC 模式中,扮演着控制器的角色,MVC 模式通过控制器实现了我们模型和视图的分
离。WebWork提供了多种活灵活视图展现方式。
我们先看看前面用户注册例子的展现方式:我们使用的是Jsp 和WebWork 自带的标签
库,Action对应的视图当然是在xwork.xml配置文件里设置:
<action name="register" class="example.register.RegisterAction" >
<result name="success" type="dispatcher">
<param name="location">register-result.jsp</param>
</result>
<interceptor-ref name="params"/>
</action>
Result是Action执行完返回的一个字符串常量,它表示Action执行完成的状态,比如:
执行成功、执行失败等。在我们前面Action的介绍中,详细介绍了它默认的标准Result,当
然Result我们也可以自己定义,只要是一个字符串常量就可以了。
Result 的值在xwork.xml 配置文件里就是result 标签里“name”的值,name="success"
表示Action执行成功,返回“success”就对应此标签的配置,进行视图输出。
“type”就是我们的Result Type,Result Type是一个类,它在Action执行完成并返回
Result 之后,决定采用哪一种视图技术,将执行结果展现给用户。我们输出的类型是:
type="dispatcher",它对应com.opensymphony.webwork.dispatcher.ServletDispatcherResult这个
类,它将执行结果通过javax.servlet.RequestDispatcher 的forward()或include()方法调度到Jsp
页面展现。
我们可以自己开发Result Type,实现我们需要的视图展现方式。Result Type必需要实现
com.opensymphony.xwork..Result 接口。在WebWork 中,它已经为我们提供了很多Result
Type,实现了视图部分对JSP, Velocity, FreeMarker, JasperReports,XML等的支持,具体如
下表格:
Result Type Nname Class
Dispatcher dispatcher com.opensymphony.webwork.dispatcher.ServletDispatcherRes
ult
Redirect redirect com.opensymphony.webwork.dispatcher.ServletRedirectResult
Action Chaining chain com.opensymphony.xwork.ActionChainResult
Velocity velocity com.opensymphony.webwork.dispatcher.VelocityResult
FreeMarker freemarker com.opensymphony.webwork.views.freemarker.FreemarkerRe
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
sult
JasperReports jasper com.opensymphony.webwork.views.jasperreports.JasperRepor
tsResult
XML/XSL xslt com.opensymphony.webwork.views.xslt.XSLTResult
HttpHeader com.opensymphony.webwork.dispatcher.HttpHeaderResult
Dispatcher:通过javax.servlet.RequestDispatcher的forward()或include()方法调度到页面展现,
这样的页面一般是Jsp页面。
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
例子:
<result name="success" type="dispatcher">
<param name="location">register-result.jsp</param>
</result>
也可以简单写成这样:
<result name="success" type="dispatcher">register-result.jsp</result>
Redirect:将响应重定向到浏览器指定的位置,它将会导致Action执行完成的数据丢失或不
再可用。它在程序里是通过调用javax.servlet.http.HttpServletResponse.sendRedirect(String
location)方法,将响应定向到参数location指定的、新的url中。
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
例子
<result name="success" type="redirect">
<param name="location">foo.jsp</param>
<param name="parse">false</param>
</result>
Action Chaining:一种特殊的视图结果,将Action执行完之后链接到另一个Action中继续
执行。新的Action使用上一个Action的上下文(ActionContext)。
参数(Parameters) 是否必需描 述
actionName 是将要被链接的Action名字
namespace 否被链接的Action 的命名空间(namespace),如果不设置,
默认的即是当前的命名空间
例子:
<result name="success" type="chain">
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
<param name="actionName">bar</param>
<param name="namespace">/foo</param>
</result>
将要调用的Action如下:
<action name="bar" class="myPackage.barAction">
...
</action>
Velocity:它类似Jsp的执行环境(使用JavaServlet容器),将Velocity模板转化成数据流的
形式,直接通过JavaServlet输出。
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置(一般是.vm页面)
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
__________例子:
<result name="success" type="velocity">
<param name="location">foo.vm</param>
</result>
FreeMarker:FreeMarker 是一个纯Java 模板引擎;一个普通的基于模板生成文本的工具,
它只能应用在Web 应用环境中。
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
contentType 否如果不指定,默认的是"text/html"
例子:
<result name="success" type="freemarker">foo.ftl</result>
JasperReports:将Action 执行的结果通过JasperReports 报表形式输出,可以指定
JasperReports支持的输出格式(PDF、HTML、XLS、CSV、XML等),默认是通过PDF格
式输出。
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
dataSource 是它是 Action的一个字段(通常是一个List),OGNL表达式
被用来去value stack(OgnlValueStack)重新找回这个
dataSource
format 否报表生成的数据格式,默认的是pdf
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
例子:
<result name="success" type="jasper">
<param name="location">foo.jasper</param>
<param name="dataSource">mySource</param>
<param name="format">CSV</param>
</result>
或者默认的pdf格式
<result name="success" type="jasper">
<param name="location">foo.jasper</param>
<param name="dataSource">mySource</param>
</result>
XML/XSL:将结果转换为xml 输出
参数(Parameters) 是否必需描 述
location 是执行完成之后转向的位置
parse 否默认的是“true”,如果设置为“false”,location 参数将不
会被OGNL表达式语言解析
例子:
<result name="success" type="xslt">foo.xslt</result>
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
表达式与言 EL和OGNL
OGNL 介绍
OGNL 是Object-Graph Navigation Language 的缩写,它是一种功能强大的表达式语言
(Expression Language,简称为EL),通过它简单一致的表达式语法,可以存取对象的任意
属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的
表达式去存取对象的属性。
XWork 遵循“不要重复地发明同一个轮子”的理论,它的表达式语言核心用的就是这个
OGNL。我们先来看看一个简单的例子:
还记得我们用户注册的那个例子吗?我们输入框的name用到的名字就是OGNL的表达式,
比如:用户名的输入框:“<input type="text" name="user.username">”,在用户注册成功之后
我们要显示用户注册的信息,用了“<ww:property value="user.username"/>”。Input输入框里
的“user.username”,它解析成Java 语句为:getUser().setUsername();,property 标签里的
“user.username”解析为Java 语句:getUser.getUsername();。
我们的两个表达式都是相同的,但前一个保存对象属性的值,后一个是取得对象属性的值。
表达式语言简单、易懂却又功能强大,关于OGNL更多的介绍可以去http://www.ognl.org,
那里有很详细的文档。
值堆栈-OgnlValueStack
OGNL 在框架中的应用,最主要是支持我们的值堆栈(Value Stack)——OgnlValueStack,
它主要的功能是通过表达式语言来存取对象的属性。用户界面输入数据,它会根据保存表达
式将数据依次保存到它堆栈的对象中,业务操作完成,结果数据会通过表达式被获取、输出。
还记得我们用户注册的例子吗?下面我们用一段程序来演示它向OgnlValueStack中保存、取
得数据的步骤:
// DemoRegisterValueStack
package example.register;
import com.opensymphony.xwork.util.OgnlValueStack;
/**
* @author moxie-qac
* achqian@yahoo.com.cn
*
*/
public class DemoRegisterValueStack {
public void demo(){
RegisterAction action = new RegisterAction();
OgnlValueStack valueStack= new OgnlValueStack();
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
valueStack.push(action);
valueStack.setValue("user.username","Moxie");
System.out.println("username =
"+valueStack.findValue("user.username"));
}
public static void main(String[] args) {
DemoRegisterValueStack demoValueStack = new
DemoRegisterValueStack();
demoValueStack.demo();
}
}
我们来看一看它的demo()方法:
1、创建我们的Action(RegisterAction)类的对象action,将action 对象压入堆栈
valueStack 中。在WebWrok 中Action 的创建、入栈是在DefaultActionInvocation 构
造函数中进行的,详细介绍见:ServletDispatcher 原理。
2、通过表达式语言,调用堆栈对象的get()、set()方法,设置该对象的值。
public void setValue(String expr, Object value)
语句:valueStack.setValue("user.username","Moxie");
的作用等同于:action.getUser().setUsername("Moxie");
3、通过表达式语言,去堆栈对象中查找我们前面保存的值,并在控制台打印。
valueStack.findValue("user.username")等同与语句:
action.getUser().getUsername()
最后控制台打印的结果:
username = Moxie
CompoundRoot
在 OgnlValueStack中,一个堆栈其实是一个List。查看OgnlValueStack你会发现,堆
栈就是com.opensymphony.xwork.util.CompoundRoot类的对象:
public class CompoundRoot extends ArrayList {
//~ Constructors /////////////////////////////////////
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
//~ Methods ////////////////////////////////////////////
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
我们通过表达式向堆栈对象操作时,我们并不知道堆栈中有哪些对象。OgnlValueStack
会根据堆栈由上向下的顺序(先入栈在下面,最后入栈在最上面)依次去查找与表达式匹配
的对象方法,找到即进行相应的存取操作。假设后面对象也有相同的方法,将不会__________被调用。
下面我们看一个对OgnlValueStack操作的程序,它主要演示了如何对Map对象的存取和
OgnlValueStack堆栈的原理:
/*
* Created on 2004-6-15
* DemoGroupValueStack.java
*/
package example.register;
import com.opensymphony.xwork.util.OgnlValueStack;
/**
* @author moxie-qac
* achqian@yahoo.com.cn
*
*/
public class DemoGroupValueStack {
public void demoAction(){
DemoGroupAction action = new DemoGroupAction();
OgnlValueStack valueStack= new OgnlValueStack();
valueStack.push(action);
User zhao = new User();
zhao.setUsername("zhao");
zhao.setEmail("zhao@yahoo.com.cn");
User qian = new User();
qian.setUsername("qian");
qian.setEmail("qian@yahoo.com.cn");
valueStack.setValue("users['zhao']",zhao);
valueStack.setValue("users['qian']",qian);
System.out.println("users['zhao'] =
"+valueStack.findValue("users['zhao']"));
System.out.println("users['qian'] =
"+valueStack.findValue("users['qian']"));
System.out.println("users size =
"+valueStack.findValue("users.size"));
System.out.println("allUserName[0] =
"+valueStack.findValue("allUserName[0]"));
}
public void demoModels(){
User model_a = new User();
model_a.setUsername("model_a");
User model_b = new User();
model_b.setUsername("model_b");
User model_c = new User();
model_c.setUsername("model_c");
OgnlValueStack valueStack= new OgnlValueStack();
valueStack.push(model_a);
valueStack.push(model_b);
valueStack.push(model_c);
System.out.println("username =
"+valueStack.findValue("username"));
System.out.println("[1].username =
"+valueStack.findValue("[1].username"));
System.out.println("[0].toString =
"+valueStack.findValue("[0]"));
System.out.println("[1].toString =
"+valueStack.findValue("[1]"));
System.out.println("[2].toString =
"+valueStack.findValue("[2]"));
}
public static void main(String[] args) {
DemoGroupValueStack demoValueStack = new DemoGroupValueStack();
demoValueStack.demoAction();
demoValueStack.demoModels();
}
}
/*
* Created on 2004-6-15
* DemoAction.java
*/
package example.register;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author moxie-qac
* achqian@yahoo.com.cn
*
*/
public class DemoGroupAction {
private Map users = new HashMap();
public Map getUsers(){
return this.users;
}
public List getAllUserName(){
return new ArrayList(users.keySet());
}
public String execute(){
//执行业务操作
return null;
}
public String toString(){
return users.toString();
}
}
注意:1、Map属性的存取,它的表达式语言如:users['zhao'],注意它用’’来引用HashMap
的key字符串。
2、demoModels()方法演示了OgnlValueStack 中堆栈的原理,请特别注意它的
[0].toString、[1].toString、[2].toString,它们依次调用堆栈中对象的toString()
方法,并逐一的减少堆栈最上面的对象。
控制台输出的结果如下:
users['zhao'] =
username=zhao;password=null;email=zhao@yahoo.com.cn;age=0
users['qian'] =
username=qian;password=null;email=qian@yahoo.com.cn;age=0
users size = 2
allUserName[0] = qian
username = model_c
[1].username = model_b
[0].toString = [username=model_c;password=null;email=null;age=0,
username=model_b;password=null;email=null;age=0,
username=model_a;password=null;email=null;age=0]
[1].toString = [username=model_b;password=null;email=null;age=0,
username=model_a;password=null;email=null;age=0]
[2].toString = [username=model_a;password=null;email=null;age=0]
Interceptor(拦截器)框架
Interceptor(拦截器)将Action共用的行为独立出来,在Action执行前后运行。这也就是我
们所说的AOP(Aspect Oriented Programming,面向切面编程),它是分散关注的编程方法,
它将通用需求功能从不相关类之中分离出来;同时,能够使得很多类共享一个行为,一旦行
为发生变化,不必修改很多类,只要修改这个行为就可以。
Interceptor 将很多功能从我们的Action 中独立出来,大量减少了我们Action 的代码,独立
出来的行为具有很好的重用性。XWork、WebWork 的许多功能都是有Interceptor 实现,可
以在配置文件中组装Action 用到的Interceptor,它会按照你指定的顺序,在Action 执行前
后运行。Interceptor 在框架中的应用如下图所示:
当你提交对Aciton(默认是.action结尾的Url)的请求时,ServletDispatcher 会根据你的请求,
去调度并执行相应的Action。在Action 执行之前,调用被Interceptor 截取,Interceptor 在
Action执行前后运行。
我们在用户注册的例子中就使用了取得Request 请求参数的拦截器,配置文件中
<interceptor-ref name="params"/>将拦截器params 组装到RegisterAction中。“params”在我
们的webwork-default.xml配置文件中有定义,webwork-default.xml 中拦截器的定义如下:
<interceptors>
<interceptor name="timer"
class="com.opensymphony.xwork.interceptor.TimerInterceptor"/>
<interceptor name="logger"
class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/>
<interceptor name="chain"
class="com.opensymphony.xwork.interceptor.ChainingInterceptor"/>
<interceptor name="static-params"
class="com.opensymphony.xwork.interceptor.StaticParametersInterceptor
"/>
<interceptor name="params"
class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
<interceptor name="model-driven"
class="com.opensymphony.xwork.interceptor.ModelDrivenInterceptor"/>
<interceptor name="component"
class="com.opensymphony.xwork.interceptor.component.ComponentIntercep
tor"/>
<interceptor name="token"
class="com.opensymphony.webwork.interceptor.TokenInterceptor"/>
<interceptor name="token-session"
class="com.opensymphony.webwork.interceptor.TokenSessionStoreIntercep
tor"/>
<interceptor name="validation"
class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
<interceptor name="workflow"
class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"
/>
<interceptor name="servlet-config"
class="com.opensymphony.webwork.interceptor.ServletConfigInterceptor"
/>
<interceptor name="prepare"
class="com.opensymphony.xwork.interceptor.PrepareInterceptor"/>
<interceptor name="conversionError"
class="com.opensymphony.webwork.interceptor.WebWorkConversionErrorInt
erceptor"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="static-params"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>
</interceptors>
这些都时有框架提供的默认的Interceptor,下面我来看看Interceptor 使用的步骤:
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
1、创建一个自己需要的Interceptor 类,它必需实现
com.opensymphony.xwork.interceptor.Interceptor 接口,具体的开发见下面的Interceptor
的原理。
2、在配置文件(xwork..xml)中申明这个Interceptor 类,它放在标签<interceptor />中,
同是<interceptor />标签嵌入在<interceptors />标签内部。
3、创建Interceptor 栈,使用标签:<interceptor-stack />,让一组Interceptor 可以按
次序调用。(可选)
4、指定Action 所要用到的Interceptor(前面申明过的),可以用<interceptor-ref />或
<default-interceptor-ref />标签。前面的标签指定某个Action 所用到的Interceptor,如果
Action没有被用<interceptor-ref />指定Interceptor,它将使用<default-interceptor-ref />指
定的Interceptor。
框架中给我们提供了很多实用的Interceptor,它的定义上面已经给出,它的具体功能如下:
timer:记录Action执行的时间,并做为日志信息输出;
logger:在日志信息中输出要执行的Action信息;
chain:将前一个执行结束的Action 属性设置到当前的Action 中。它被用在
ResultType 为“chain”指定结果的Action 中,该结果Action 对象会从
OgnlValueStack中获得前一个Action对应的属性,它实现Action链之间的数据
传递;
static-params:将xwork.xml 配置文件里定义的Action 参数,设置到对应的Action
中。Action参数使用<param />标签,是<action />标签的直接子元素。我们这里定
义的Action 类必需实现com.opensymphony.xwork.config.entities.
Parameterizable接口;
params:将Request请求的参数设置到相应Action对象的属性中,用户注册例子用
到过这个拦截器;
model-driven:如果Action 实现ModelDriven 接口,它将getModel()取得的模型对
象存入OgnlValueStack中;
component:激活组件功能支持,让注册过的组件在当前Action中可用,即为Action
提供IoC(依赖倒转控制)框架的支持;
token:核对当前Action 请求(request)的有效标识,防止重复提交Action 请求
(request)。
token-session:功能同上,但是当提交无效的Action请求标识时,它会将请求数据
保存到session中。
validation:实现使用xml 配置文件({Action}-validation.xml)对Action 属性值进
行验证,详细请看后面介绍的验证框架。
workflow:调用Action 类的验证功能,假设Action 使用ValidationAware实现
验证(ActionSupport提供此功能),如果验证没有通过,workflow会将请求返回到
input 视图(Action的<result />中定义的)。
servlet-config:提供Action 直接对HttpServletRequest 或HttpServletResponse 等
JavaServlet api的访问,Action要实现相应的接口,例如:ServletRequestAware
或ServletResponseAware等。如果必需要提供对JavaServlet api的访问,我们建
议使用ServletActionContext,在前面ActionContext章节中有介绍。
prepare:在Action执行之前调用Action的prepare()方法,这个方法是用来准备
Action 执行之前要做的工作。它要求我们的Action 必需实现
com.opensymphony.xwork. Preparable接口
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
conversionError:用来处理框架进行类型转化(Type Conversion)时的出错信息。它将
存储在ActionContext 中的类型转化(Type Conversion)错误信息转化成相应的
Action字段的错误信息,保存在堆栈中。根据需要,可以将这些错误信息在视图中
显示出来。
Interceptor 的原理
下面我们来看看Interceptor 是如何实现在Action执行前后调用的:
Action 和Interceptor 在框架中的执行,是由ActionInvocation 对象调用的。它是用方法:
String invoke() throws Exception;来实现的,它首先会依次调用Action 对应的
Interceptor,执行完成所有的Interceptor 之后,再去调用Action的方法,代码如下:
if (interceptors.hasNext()) {
Interceptor interceptor = (Interceptor) interceptors.next();
resultCode = interceptor.intercept(this);
} else {
if (proxy.getConfig().getMethodName() == null) {
resultCode = getAction().execute();
} else {
resultCode = invokeAction(getAction(), proxy.getConfig());
}
}
它会在拦截器栈中遍历Interceptor,调用Interceptor 的方法:
String intercept(ActionInvocation invocation) throws Exception;。
我们一直都提到,Interceptor 是在Action 前后执行,可是从上面的代码我们看到的却是执行
完所有Interceptor 的intercept()方法之后再去调用我们的Action。“在Action前后执行”
是如何实现的呢?我们来看看抽象类AroundInterceptor的intercept()实现:
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
before(invocation);
result = invocation.invoke();
after(invocation, result);
return result;
}
原来在intercept()方法又对ActionInvocation 的invoke()方法进行递归___________调用,
ActionInvocation 循环嵌套在intercept() 中, 一直到语句result =
invocation.invoke();执行结束,即:Action执行完并返回结果result,这时Interceptor
对象会按照刚开始执行的逆向顺序依次执行结束。这样before()方法将在Action执行前
调用,after()方法在Action执行之后运行。
WebWork教程
有任何意见和建议,请email to:achqian@hotmail.com(MSN)
版权所有,请勿转载和篡改
验证框架
WebWork提供了在Action执行之前,对输入数据的验证功能,它使用了其核心XWork的验
证框架。提供了如下功能:
1、可配置的验证文件。它的验证文件是一个独立的XML 配置文件,对验证的添加、修改
只需更改配置文件,无需编译任何的Class。
2、验证文件和被验证的对象完全解藕。验证对象是普通的JavaBean 就可以了(可以是
FormBean、域对象等),它们不需实现任何额外的方法或继承额外的类。
3、多种不同的验证方式。因为它验证功能是可以继承的,所以可以用多种不同的方式指定
验证文件,比如:通过父类的Action、通过Action、通过Action 的方法、通过Action
所使用的对象,等等。
4、强大的表达式验证。它使用了OGNL的表达式语言,提供强大的表达式验证功能。
5、同时支持服务器端和客户端验证。
为用户注册添加验证功能
下面我们来看看如何为用户注册添加验证功能:
1、注册我们的验证类型
WebWork为不同的验证要求提供不同的验证类型。一个验证类型,一般是有一个类来
提供。这个类必须实现接口:com.opensymphony.xwork.validator.Validator,但我
们在写自己的验证类型时,无需直接实现Validator 接口,它有抽象类可供直接使用如
ValidatorSupport、FieldValidatorSupport等。
验 证 类型在使用之前, 必须要在ValidatorFactory
(com.opensymphony.xwork.validator. ValidatorFactory)中注册。可以有二种
方法实现验证类型的注册。一、写程序代码进行注册,它使用ValidatorFactory类的静
态方法:registerValidator(String name, String className)。二、使用配置文
件validators.xml 进行注册,要求把文件validators.xml 放到ClassPath 的跟目录中
(/WEB-INF/classes)。但在实际开发中,一般都使用第二中注册方法。我们的验证类型注册
如下:
<validators>
<validator name="required"
class="com.opensymphony.xwork.validator.validators.RequiredFieldValid
ator"/>
<validator name="requiredstring"
class="com.opensymphony.xwork.validator.validators.RequiredStringVali
dator"/>
<validator name="int"
class="com.opensymphony.xwork.validator.validators.IntRangeFieldValid
ator"/>
<validator name="date"
class="com.opensymphony.xwork.validator.validators.DateRangeFieldVali
dator"/>
<validator name="expression"
class="com.opensymphony.xwork.validator.validators.ExpressionValidato
r"/>
<validator name="fieldex
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics