ServletDispatcher
是默认的处理
Web Http
请求的调度器,它是一个
JavaServlet
,是
WebWork
框架的控制器。所有对
Action
调用的请求都将通过这个
ServletDispatcher
调度。它将在
web.xml
里配置
ServletDispatcher
时指定,让所有对
WebWork
的
Action(
默认的是
.action
的后缀
)
的请求都对应到该调度的
JavaServlet
中,具体配置在前面的
WebWork-helloWorld
中有介绍。
ServletDispatcher
接受客户端的
HTTP
请求,将
JavaServlet
的很多相关对象进行包装,再传给我们的
XWork
框架,由我们的
XWork
框架去解析我们的
xwork.xml
配置文件,根据配置文件的信息,创建对应的
Action
,组装并调用相应的拦截器,执行
Action
,返回执行结果。
WebWork
使用
XWork
的核心,主要是由这个
ServletDispatcher
去实现的
具体调用流程:
(Servlet
调用流程
)
一、
init
()方法在
web
服务器启动时调用。
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
进行包装的:
Request
包装
protected Map getParameterMap(HttpServletRequest request) throws IOException {
return request.getParameterMap();
}
这个方法比较简单,它直接调用了
HttpServletRequest
的方法
getParameterMap
(),将所有
request
请求的参数封装到一个
Map
中
Session
包装
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
,它的构造函数操作有
:
i.
由
com.opensymphony.xwork.ObjectFactory
创建我们配置文件描述的
Action
对象。再将这个
Action
对象存放入
OgnlValueStack
中。记得我们前面用户注册的例子吗?当用户提交表达时它会有表达式语言向
OgnlValueStack
取得
Action
对象的字段,再把输入框的数据设置到对应的
Action
字段中,这个
Action
对象就是在这个时候进栈的
ii.
传入
extraContext
参数,创建与
ActionInvocation
对应的
Action
上下文(
ActionContext
)。记得我们在介绍
ActionContext
的最后,提出了一个需要注意的地方:不要在
Action
构造函数中调用
ActionContext.getContext()
。现在应该能明白,原来是
Action
对象实例在
ActionContext
对象实例之前创建的,所有这样取得
ActionContext
容器对象就有可能会返回
null
iii.
取得这个
Action
对应的所有拦截器(
Interceptor
),存放入
java.util.Iterator
对象中。
5、
执行
proxy
的
execute()
方法,这个方法最核心的语句是:
retCode = invocation.invoke();
,
invocation
对象的
invoke()
方法它遍历并执行这个
Action
对应的所有拦截器,执行
Action
对应的方法(默认的是
execute()
),根据
Action
执行返回的值去调用执行相应的
Result
(返回结果处理)的方法
理解了
ServletDispatcher
,我们就明白了整个框架调用执行的顺序。
Action
虽然是与
Web
无关,可是它的创建、参数设置、执行与我们的
WebWork
、
XWork
紧密关联在一起,有我们的控制器
ServletDispatcher
去统一调度,那我们如何去对
Action
进行独立的单元测试呢?
请看下面的例子:使用单元测试框架
JUnit
对
register.User. RegisterAction
做单元测试
见
example.register. RegisterActionTest
类
testExecuteWithProxyFactory()
方法
public
void testExecuteWithProxyFactory() throws Exception{
//
创建
action
上下文(
actionContext
)
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);
//
创建代理(找出所在的
action
)
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy("example", "register", extraContext);
//
不要执行出现
Success
后的代码
proxy.setExecuteResult(false);
//
测试代码是否正确
assertEquals(proxy.execute(),"success");
//
返回
Action
,测试
action
里面的代码是否正确
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.ServletDispatcherResult
|
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.FreemarkerResult
|
JasperReports
|
Jasper
|
com.opensymphony.webwork.views.jasperreports.JasperReportsResult
|
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>
Action Chaining
:
一种特殊的视图结果,将
Action
执行完之后链接到另一个
Action
中继续执行。新的
Action
使用上一个
Action
的上下文(
ActionContext
)。
参数
(Parameters)
|
是否必需
|
描
述
|
actionName
|
是
|
将要被链接的
Action
名字
|
namespace
|
否
|
被链接的
Action
的命名空间(
namespace
),如果不设置,默认的即是当前的命名空间
|
例子:
<result name="success" type="chain">
<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
|
例子:
<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>