﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-wash-随笔分类-strut、webwork、jsf、tapestry、echo</title><link>http://www.blogjava.net/wash/category/3740.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 03:21:16 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 03:21:16 GMT</pubDate><ttl>60</ttl><item><title>webworks chapter 1 -夏昕Webwork2 Developer’s Guide Version 1.0</title><link>http://www.blogjava.net/wash/archive/2005/10/08/14974.html</link><dc:creator>wash</dc:creator><author>wash</author><pubDate>Sat, 08 Oct 2005 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/wash/archive/2005/10/08/14974.html</guid><wfw:comment>http://www.blogjava.net/wash/comments/14974.html</wfw:comment><comments>http://www.blogjava.net/wash/archive/2005/10/08/14974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wash/comments/commentRss/14974.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wash/services/trackbacks/14974.html</trackback:ping><description><![CDATA[<BR>ServletDispatcher 接受到Servlet Container 传递过来的请求，将进行一下几个动作：<BR>1. 从请求的服务名（/login.action）中解析出对应的Action名称（login）<BR>2. 遍历HttpServletRequest、HttpSession、ServletContext 中的数据，并将其复制到<BR>Webwork的Map实现中，至此之后，所有数据操作均在此Map结构中进行，从<BR>而将内部结构与Servlet API相分离。<BR>至此，Webwork 的工作阶段结束，数据将传递给XWork 进行下一步处理。从这里也可以看到Webwork和xwork之间的切分点，Webwork为xwork提供了一个面向Servlet 的协议转换器，将Servlet 相关的数据转构转换成xwork所需要的通用数据格式，而xwork将完成实际的服务调度和功能实现。<BR>这样一来，以xwork为核心，只需替换外围的协议转换组件，即可实现不同技术平台之间的切换（如将面向Servlet的Webwork替换为面向JMS的协议转换器实现，即可在保留应用逻辑实现的情况下，实现不同外部技术平台之间的移植）。<BR>3. 以上述信息作为参数，调用ActionProxyFactory创建对应的ActionProxy实例。<BR>ActionProxyFactory 将根据Xwork 配置文件（xwork.xml）中的设定，创建<BR>ActionProxy实例，ActionProxy中包含了Action的配置信息（包括Action名称，<BR>对应实现类等等）。<BR>4. ActionProxy创建对应的Action实例，并根据配置进行一系列的处理程序。包括<BR>执行相应的预处理程序（如通过Interceptor 将Map 中的请求数据转换为Action<BR>所需要的Java 输入数据对象等），以及对Action 运行结果进行后处理。<BR>ActionInvocation 是这一过程的调度者。而com.opensymphony.xwork.<BR>DefaultActionInvocation 则是XWork 中对ActionInvocation 接口的标准实现，如<BR>果有精力可以对此类进行仔细研读，掌握了这里面的玄机，相信XWork的引擎<BR>就不再神秘。<BR>下面我们来看配置文件：<BR>xwork.xml：<BR>&lt;!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN"<BR>"<A href="http://www.opensymphony.com/xwork/xwork-1.0.dtd">http://www.opensymphony.com/xwork/xwork-1.0.dtd</A>"&gt;<BR>&lt;<BR>xwork&gt;<BR>&lt;include file="webwork-default.xml" /&gt; ⑴<BR>&lt;package name="default" extends="webwork-default"&gt; ⑵<BR>&lt;action name="login" ⑶<BR>class="net.xiaxin.webwork.action.LoginAction"&gt;<BR>&lt;result name="success" type="dispatcher"&gt; ⑷<BR>&lt;param name="location"&gt;/main.jsp&lt;/param&gt;<BR>&lt;/result&gt;<BR>&lt;result name="loginfail" type="dispatcher"&gt;<BR>&lt;param name="location"&gt;/index.jsp&lt;/param&gt;<BR>&lt;/result&gt;<BR>&lt;interceptor-ref name="params" /&gt; ⑸<BR>&lt;interceptor-ref name="model-driven"/&gt; ⑹<BR>&lt;/action&gt;<BR>&lt;/package&gt;<BR>&lt;/xwork&gt;<BR>⑴ include<BR>通过include 节点，我们可以将其他配置文件导入到默认配置文件xwork.xml 中。<BR>从而实现良好的配置划分。<BR>这里我们导入了Webwork 提供的默认配置webwork-default.xml（位于<BR>webwork.jar 的根路径）。<BR>⑵ package<BR>XWork中，可以通过package对action进行分组。类似Java 中package和class的<BR>关系。为可能出现的同名Action提供了命名空间上的隔离。<BR>同时，package还支持继承关系。在这里的定义中，我们可以看到：<BR>extends="webwork-default"<BR>"webwork-default"是webwork-default.xml文件中定义的package，这里通<BR>过继承，"default" package 自动拥有"webwork-default" package 中的所有<BR>定义关系。这个特性为我们的配置带来了极大便利。在实际开发过程中，我们可以根据自身的应用特点，定义相应的package模板，并在各个项目中加以重用，无需再在重复繁琐的配置过程中消耗精力和时间。<BR>此外，我们还可以在Package节点中指定namespace，将我们的action分为若干个<BR>逻辑区间。如：<BR>&lt;package name="default" namespace="/user"<BR>extends="webwork-default"&gt;<BR>就将此package中的action定义划归为/user 区间，之后在页面调用action的时候，<BR>我们需要用/user/login.action 作为form action 的属性值。其中的/user/就指定了此<BR>action的namespace，通过这样的机制，我们可以将系统内的action进行逻辑分类，<BR>从而使得各模块之间的划分更加清晰。<BR>⑶ action<BR>Action配置节点，这里可以设定Action的名称和对应实现类。<BR>⑷ result<BR>通过result 节点，可以定义Action 返回语义，即根据返回值，决定处理模式以及<BR>响应界面。<BR>这里，返回值"success"（Action 调用返回值为String 类型）对应的处理模式为<BR>"dispatcher"。<BR>可选的处理模式还有：<BR>1. dispatcher<BR>本系统页面间转向。类似forward。<BR>2. redirect<BR>浏览器跳转。可转向其他系统页面。<BR>3. chain<BR>将处理结果转交给另外一个Action处理，以实现Action的链式处理。<BR>4. velocity<BR>将指定的velocity模板作为结果呈现界面。<BR>5. xslt<BR>将指定的XSLT 作为结果呈现界面。<BR>随后的param节点则设定了相匹配的资源名称。<BR>⑷ interceptor-ref<BR>设定了施加于此Action的拦截器（interceptor）。关于拦截器，请参见稍后的“XWork拦截器体系”部。<BR>interceptor-ref定义的是一个拦截器的应用，具体的拦截器设定，实际上是继<BR>承于webwork-default package，我们可以在webwork-default.xml 中找到<BR>对应的"params"和"model-driven"拦截器设置：<BR>&lt;interceptors&gt;<BR>……<BR>&lt;interceptor name="params"class="com.opensymphony.xwork.interceptor.ParametersInt<BR>erceptor" /&gt;<BR>&lt;interceptor name="model-driven"<BR>class="com.opensymphony.xwork.interceptor.ModelDrivenIn<BR>terceptor" /&gt;<BR>……<BR>&lt;/interceptors&gt;<BR>"params"大概是Webwork 中最重要、也最常用的一个Interceptor。上面曾经将<BR>MVC工作流程划分为几个步骤，其中的第一步:<BR>“将Web 页面中的输入元素封装为一个（请求）数据对象”<BR>就是通过"params"拦截器完成。Interceptor 将在Action 之前被调用，因而，<BR>Interceptor 也成为将Webwork传来的MAP 格式的数据转换为强类型Java 对象的<BR>最佳实现场所。<BR>"model-driven"则是针对Action 的Model驱动模式的interceptor 实现。具体描<BR>述请参见稍后的“Action驱动模式”部分很可能我们的Action 都需要对这两个interceptor 进行引用。我们可以定义一个interceptor-stack，将其作为一个interceptor 组合在所有Action 中引用。如，上面的配置文件可修改为：<BR>&lt;xwork&gt;<BR>&lt;include file="webwork-default.xml" /&gt;<BR>&lt;package name="default" extends="webwork-default"&gt;<BR>&lt;interceptors&gt;<BR>&lt;interceptor-stack name="modelParamsStack"&gt;<BR>&lt;interceptor-ref name="params" /&gt;<BR>&lt;interceptor-ref name="model-driven" /&gt;<BR>&lt;/interceptor-stack&gt;<BR>&lt;/interceptors&gt;<BR>&lt;action name="login" class="net.xiaxin.webwork.action.LoginAction"&gt;<BR>&lt;result name="success" type="dispatcher"&gt;<BR>&lt;param name="location"&gt;/main.jsp&lt;/param&gt;<BR>&lt;/result&gt;<BR>&lt;result name="loginfail" type="dispatcher"&gt;<BR>&lt;param name="location"&gt;/index.jsp&lt;/param&gt;<BR>&lt;/result&gt;<BR>&lt;interceptor-ref name="modelParamsStack" /&gt;<BR>&lt;/action&gt;<BR>&lt;/package&gt;<BR>&lt;/xwork&gt;<BR>通过引入interceptor-stack，我们可以减少interceptor 的重复申明。<BR>下面是我们的Model对象：<BR>LoginInfo.java：<BR>public class LoginInfo {<BR>private String password;<BR>private String username;<BR>private List messages = new ArrayList();<BR>private String errorMessage;<BR>public List getMessages() {<BR>return messages;<BR>}<BR>public String getErrorMessage() {<BR>return errorMessage;<BR>}<BR>public void setErrorMessage(String errorMessage) {<BR>this.errorMessage = errorMessage;<BR>}<BR>public String getPassword() {<BR>return password;<BR>}<BR>public void setPassword(String password) {<BR>this.password = password;<BR>}<BR>public String getUsername() {<BR>return username;<BR>}<BR>public void setUsername(String username) {<BR>this.username = username;<BR>}<BR>}<BR>很简单，这只是一个纯粹的值对象（Value-Object）。这里，它扮演着模型（Model）的角色，并与Action的输入输出密切相关。与Spring MVC中的Command对象不同，Webwork 中的Model对象，扮演着承上启下的角色，它既是Action的输入参数，又包含了Action处理的结果数据。<BR>换句话说，输入的Http请求参数，将被存储在Model对象传递给Action进行处理，Action处理完毕之后，也将结果数据放置到Model 对象中，之后，Model 对象与返回界面融合生成最后的反馈页面。也正由于此，笔者建议在实际开发中采用Model-Driven 模式，而非Property-Driven 模式（见稍后“Action驱动模式”部分），这将使得业务逻辑更加清晰可读。<BR>对应的Action代码<BR>public class LoginAction implements Action, ModelDriven {<BR>private final static String LOGIN_FAIL="loginfail";<BR>LoginInfo loginInfo = new LoginInfo();<BR>public String execute() throws Exception {<BR>if ("erica".equalsIgnoreCase(loginInfo.getUsername())<BR>&amp;&amp; "mypass".equals(loginInfo.getPassword())) {<BR>//将当前登录的用户名保存到Session<BR>ActionContext ctx = ActionContext.getContext();<BR>Map session = ctx.getSession();<BR>session.put("username",loginInfo.getUsername());<BR>//出于演示目的，通过硬编码增加通知消息以供显示<BR>loginInfo.getMessages().add("message1");<BR>loginInfo.getMessages().add("message2");<BR>loginInfo.getMessages().add("message3");<BR>return SUCCESS;<BR>}else{<BR>loginInfo.setErrorMessage("Username/Password Error!");<BR>return LOGIN_FAIL;<BR>}<BR>}<BR>public Object getModel() {<BR>return loginInfo;<BR>}<BR>}<BR>可以看到，LoginAction实现了两个接口：<BR>1. Action<BR>Action接口非常简单，它指定了Action的入口方法（execute），并定义了<BR>几个默认的返回值常量：<BR>public interface Action extends Serializable {<BR>public static final String SUCCESS = "success";<BR>public static final String NONE = "none";<BR>public static final String ERROR = "error";<BR>public static final String INPUT = "input";<BR>public static final String LOGIN = "login";<BR>public String execute() throws Exception;<BR>}<BR>private final static String LOGIN_FAIL="loginfail";<BR>LoginInfo loginInfo = new LoginInfo();<BR>public String execute() throws Exception {<BR>if ("erica".equalsIgnoreCase(loginInfo.getUsername())<BR>&amp;&amp; "mypass".equals(loginInfo.getPassword())) {<BR>//将当前登录的用户名保存到Session<BR>ActionContext ctx = ActionContext.getContext();<BR>Map session = ctx.getSession();<BR>session.put("username",loginInfo.getUsername());<BR>//出于演示目的，通过硬编码增加通知消息以供显示<BR>loginInfo.getMessages().add("message1");<BR>loginInfo.getMessages().add("message2");<BR>loginInfo.getMessages().add("message3");<BR>return SUCCESS;<BR>}else{<BR>loginInfo.setErrorMessage("Username/Password Error!");<BR>return LOGIN_FAIL;<BR>}<BR>}<BR>public Object getModel() {<BR>return loginInfo;<BR>}<BR>}<BR>可以看到，LoginAction实现了两个接口：<BR>1. Action<BR>Action接口非常简单，它指定了Action的入口方法（execute），并定义了<BR>几个默认的返回值常量：<BR>public interface Action extends Serializable {<BR>public static final String SUCCESS = "success";<BR>public static final String NONE = "none";<BR>public static final String ERROR = "error";<BR>public static final String INPUT = "input";<BR>public static final String LOGIN = "login";<BR>public String execute() throws Exception;<BR>}<BR>SUCCESS、NONE、ERROR、INPUT、LOGIN 几个字符串常量定义了常用的<BR>几类返回值。我们可以在Action 实现中定义自己的返回类型，如本例中的<BR>LOGIN_FAIL定义。<BR>而execute方法，则是Action的入口方法，XWork将调用每个Action的execute<BR>方法以完成业务逻辑处理。<BR>2. ModelDriven<BR>ModelDriven接口更为简洁：<BR>public interface ModelDriven {<BR>Object getModel();<BR>}<BR>ModelDriven仅仅定义了一个getModel方法。XWork在调度Action时，将通<BR>过此方法获取Model 对象实例，并根据请求参数为其设定属性值。而此后的<BR>页面返回过程中，XWork 也将调用此方法获取Model 对象实例并将其与设定<BR>的返回界面相融合。<BR>注意这里与Spring MVC 不同，Spring MVC 会自动为逻辑处理单元创建<BR>Command Class实例，但Webwork不会自动为Action创建Model对象实例，<BR>Model 对象实例的创建需要我们在Action 代码中完成（如LoginAction 中<BR>LoginInfo对象实例的创建）。<BR>另外，如代码注释中所描述，登录成功之后，我们随即将username保存在Session之中，这也是大多数登录操作必不可少的一个操作过程。<BR>这里面牵涉到了Webwork中的一个重要组成部分：ActionContext。<BR>ActionContext为Action提供了与容器交互的途径。对于Web 应用而言，与容器的交互大多集中在Session、Parameter，通过ActionContext我们在代码中实现与Servlet API无关的容器交互。<BR>如上面代码中的：<BR>ActionContext ctx = ActionContext.getContext();<BR>Map session = ctx.getSession();<BR>session.put("username",loginInfo.getUsername());<BR>同样，我们也可以操作Parameter:<BR>ActionContext ctx = ActionContext.getContext();<BR>Map params = ctx.getParameters();<BR>String username = ctx.getParameters("username");<BR>上述的操作，将由XWork根据当前环境，调用容器相关的访问组件（Web 应用对应的就是Webwork）完成。上面的ActionContext.getSession()，XWork 实际上将通过Webwork提供的容器访问代码“HttpServletRequest.getSession()”完成。<BR>注意到，ActionContext.getSession返回的是一个Map类型的数据对象，而非HttpSession。这是由于WebWork对HttpSession进行了转换，使其转变为与Servlet API无关的Map对象。通过这样的方式，保证了Xwork 所面向的是一个通用的开放结构。从而使得逻辑层与表现层无关。增加了代码重用的可能。此外， 为了提供与Web 容器直接交互的可能。WebWork 还提供了一个ServletActionContext实现。它扩展了ActionContext，提供了直接面向Servlet API的容器访问机制。<BR>我们可以直接通过ServletActionContext.getRequest 得到当前HttpServletRequest 对象的引用，从而直接与Web 容器交互。<BR>获得如此灵活性的代价就是，我们的代码从此与ServletAPI 紧密耦合，之后系统在不同平台之间移植就将面临更多的挑战（同时单元测试也难于进行）。<BR>平台移植的需求并不是每个应用都具备。大部分系统在设计阶段就已经确定其运行平台，且无太多变更的可能。不过，如果条件允许，尽量通过ActionContext 与容器交互，而不是平台相关的ServletActionContext，这样在顺利实现功能的同时，也获得了平台迁移上的潜在优势，何乐而不为。<BR>登录成功界面：<BR>main.jsp:<BR>&lt;%@ taglib prefix="ww" uri="webwork"%&gt;<BR>&lt;html&gt;<BR>&lt;body&gt;<BR>&lt;p align="center"&gt;Login Success!&lt;/p&gt;<BR>&lt;p align="center"&gt;Welcome!<BR>&lt;ww:property value="#session['username']"/&gt;<BR>&lt;/p&gt;<BR>&lt;p align="center"&gt;<BR>&lt;b&gt;Messages:&lt;/b&gt;&lt;br&gt;<BR>&lt;ww:iterator value="messages" status="index"&gt;<BR>&lt;ww:if test="#index.odd == true"&gt;<BR>!&lt;ww:property/&gt;&lt;br&gt;<BR>&lt;/ww:if&gt;<BR>&lt;ww:else&gt;<BR>*&lt;ww:property/&gt;&lt;br&gt;<BR>&lt;/ww:else&gt;<BR>&lt;/ww:iterator&gt;<BR>&lt;/p&gt;<BR>&lt;/body&gt;<BR>&lt;/html&gt;<BR>这里我们引入了Webwork的taglib，如页面代码第一行的申明语句。<BR>下面主要使用了三个tag:<BR>Ø &lt;ww:property value="#session['username']"/&gt;<BR>读取Model对象的属性填充到当前位置。<BR>value指定了需要读取的Model对象的属性名。<BR>这里我们引用了LoginAction在session中保存的’username’对象。<BR>由于对应的Model（LoginInfo）中也保存了username 属性。下面的语句与之<BR>效果相同：<BR>&lt;ww:property value="username"/&gt;<BR>与JSP2 中的EL类似，对于级联对象，这里我们也可以通过“.”操作符获得<BR>其属性值，如value="user.username"将得到Model 对象中所引用的user<BR>对象的username 属性（假设LoginInfo中包含一个User 对象，并拥有一个名<BR>为“username”的属性）。<BR>关于EL的内容比较简单，本文就不再单独开辟章节进行探讨。<BR>Webwork中包括以下几种特殊的EL表达式：<BR>² parameter[‘username’] 相当于request.getParameter(“username”);<BR>² request[‘username’] 相当于request.getAttribute(“username”);<BR>² session[‘username’] 从session中取出以“username”为key的值<BR>² application[‘username’] 从ServletContext中取出以“username”为key<BR>的值<BR>注意需要用“#”操作符引用这些特殊表达式。<BR>另外对于常量，需要用单引号包围，如#session['username'] 中的<BR>'username'。<BR>Ø &lt;ww:iterator value="messages" status="index"&gt;<BR>迭代器。用于对java.util.Collection、java.util.Iterator、java.util.Enumeration,、<BR>java.util.Map、Array类型的数据集进行循环处理。<BR>其中，value属性的语义与&lt;ww:property&gt;中一致。<BR>而status属性则指定了循环中的索引变量，在循环中，它将自动递增。<BR>而在下面的&lt;ww:if&gt;中，我们通过“#”操作符引用这个索引变量的值。<BR>索引变量提供了以下几个常用判定方法：<BR>² first 当前是否为首次迭代<BR>² last 当前是否为最后一次迭代<BR>² odd 当前迭代次数是否奇数<BR>² even 当前迭代次数是否偶数<BR>Ø &lt;ww:if test="#index.odd == true"&gt;和&lt;ww:else&gt;<BR>用于条件判定。<BR>test属性指定了判定表达式。表达式中可通过“#”操作符对变量进行引用。<BR>表达式的编写语法与java 表达式类似。<BR>类似的，还有&lt;ww:elseif test="……"&gt;。<BR><BR>登录失败界面<BR>实际上，这个界面即登录界面index.jsp。只是由于之前出于避免干扰的考虑，隐藏了index.jsp中显示错误信息的部分。<BR>完整的index.jsp如下：<BR>&lt;%@ page pageEncoding="gb2312"<BR>contentType="text/html;charset=gb2312"%&gt;<BR>&lt;%@ taglib prefix="ww" uri="webwork"%&gt;<BR>&lt;html&gt;<BR>&lt;body&gt;<BR>&lt;form action="/login.action"&gt;<BR>&lt;p align="center"&gt;<BR>登录&lt;br&gt;<BR>&lt;ww:if test="errorMessage != null"&gt;<BR>&lt;font color="red"&gt;<BR>&lt;ww:property value="errorMessage"/&gt;<BR>&lt;/font&gt;<BR>&lt;/ww:if&gt;<BR>&lt;/p&gt;<BR>用户名:<BR>&lt;input type="text" name="model.username" /&gt;<BR>&lt;br&gt;<BR>密码:<BR>&lt;input type="password" name="model.password" /&gt;<BR>&lt;br&gt;<BR>&lt;p align="center"&gt;<BR>&lt;input type="submit" value="提交" name="B1"/&gt;<BR>&lt;input type="reset" value="重置" name="B2"/&gt;<BR>&lt;/p&gt;<BR>&lt;/form&gt;<BR>&lt;/body&gt;<BR>&lt;/html&gt;<BR>这里首先我们进行判断，如果Model中的errorMessage不为null，则显示错误信息。这样，在用户第一次登录时，由于Model对象尚未创建，errorMessage自然为null，错误信息不会显示，即得到了与之前的index.jsp同样的效果。<img src ="http://www.blogjava.net/wash/aggbug/14974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wash/" target="_blank">wash</a> 2005-10-08 11:23 <a href="http://www.blogjava.net/wash/archive/2005/10/08/14974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>