Vincent

Vicent's blog
随笔 - 74, 文章 - 0, 评论 - 5, 引用 - 0
数据加载中……

WebWork教程-ServletDispatcher

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 (返回结果处理)的方法
 
Action 的单元测试
理解了 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>  

posted on 2006-09-01 13:41 Binary 阅读(639) 评论(0)  编辑  收藏 所属分类: Webwork


只有注册用户登录后才能发表评论。


网站导航: