《.NET程序员开始学JAVA》之 webwork 篇

       接着上一次的环境篇,今天开始我们开始来看看MVC的V,开始进入实质性的内容。
       在接触webwork之前,我没Struts的知识,连jsp的也只有很简单的几个print,不过我的asp.net知识应该还算可以,坚信百变不离其宗的宗旨,应该吧难理解webwork这个容器框架的工作原理。
        简介:  WebWork是由OpenSymphony组织开发的,致力于组件化和代码重用的拉出式MVC模式J2EE Web框架。WebWork目前最新版本是2.2.6,现在的WebWork2.x前身是Rickard Oberg开发的WebWork,但现在WebWork已经被拆分成了Xwork1和WebWork2两个项目, 
       Xwork简洁、灵活功能强大,它是一个标准的模式实现,并且完全从层脱离出来Xwork提供了很多核心功能:前端拦截机,运行时表单属性验证,类型转换,强大的表达式语言(OGNL – the Object Graph Notation Language),IoCInversion of Control倒置控制)容器等(在)。 WebWork2建立在Xwork之上,处理HTTP的响应和请求。WebWork2使用ServletDispatcher将HTTP请求的变成Action(业务层Action类), session(会话)application(应用程序)范围的映射,request请求参数映射。WebWork2支持多视图表示,视图部分可以使用JSP, Velocity, FreeMarker, JasperReports,XML等。
    在展开对webwork的正式叙述之前,有必要提一下webwork与struts之间的关系,实际上在webwork2.2以前,webwork与struts的联系就是他们是由同一个组织开发的两套框架。从官方的一个文档里可以看出二者的一番较量:
  

Struts1.1

WebWork2

Action

Struts里面,每一个Action类必需要继承一个抽象的类org.apache.struts.action.Action。这个在Java编程中会引来一些问题,就是关于多种继承的问题。

WebWorkAction类仅需要实现接口com.opensymphony.xwork.Action,也可以实现其它的接口来实现更多的功能,譬如:validate(验证),localware(国际化)等。当然,它也提供了一个类ActionSupport集成了上面的所有功能,我们在开发中可以根据需要选择。

线程模型

Struts Action必需是threadsafe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。

WebWork中,每个请求对应一个Action,因此没有线程的安全问题。实际上Servlet容器对每个请求也产生多个对象,它也没有证明对性能和垃圾回收产生太多的影响。

Servlet的依赖

Struts处理Action时必需要依赖ServletRequest ServletResponse,所有它摆脱不了Servlet容器。

WebWorkAction不用依赖Web层和其它的容器。它可以通过ActionContext,直接去访问RequestResponse,但这个是可选的,只有在必需的请求下使用。

测试

Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。

Webworkaction能够通过赋予一定的属性,就可以执行单元测试。同时也可以使用一个mock的实例去测试,而不是通过启动web容器来进行测试。

FormBean

Struts要求有FormBean对应每一个表单,而且FormBean必需继承抽象类ActionForm。而使用DynaBeans实际上没有太大的意义。不能够很好的处理现有的模型。

Webwork 能够动态的收集web的数据然后再赋值给bean。它也可以使用FormBean的形式,FormBean可以是普通的DTO和域对象,它不用重新根据域对象来生成新的FormBean,也不需继承抽象类ActionForm

前端表达式语言

Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。

WebWork的表达式语言使用了功能强大的OGNL。它使用OGNL建立一个OgnlValueStack来搜索数据。Webwork前端也可以使用JSTL,但它同时支持:velocityfreemakerjspparerxml

类型的转换

StrutsFormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。

WebWork使用OGNL进行类型转化,提供了所有基本类型的转化功能。类型转化可以直接对一个Class进行(Class级别)转化,也可以对Class的字段进行类型转化。它使用拦截器可以很容易的将类型转化的错误信息返回给用户,而且错误信息可以对应到一个相应的字段。

Action 执行前和后的处理

Struts处理Action的时候是基于classhierarchies,很难在action处理前和后进行操作。

Webwork2 允许您处理Action可以通过拦截器,就是在每一个Action处理前或者后进行其它操作。它的拦截器可以在配置文件中动态添加,这样Action和拦截器之间完全解藕,更好的实现了组件化。

验证处理

Struts的验证是调用FormBeanvalidator()方法,其实就是对FormBean的验证。它一般使用框架Commons Validation进行数据验证处理。它使用了一个全局的配置文件validation.xml定义了FormBean的验证信息。StrutsFormBean属性都被认为是String类型,所以它在验证时也需要额外的类型转化。

WebWork使用Xwork的验证框架进行验证处理,它可以通过配置拦截器来激活。它可以为每个需要验证的Class指定一个xml验证文件,也可以为一个Class在不同的情况指定不同的xml验证文件。WebWork证可以给每个Action类指定对应的验证文件,也可以给Action的字段去指定验证文件。通过拦截器来组装Action和其验证文件,使它们之间完全解藕。

Action执行的控制

Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。

在这个方面,WebWork的拦截器栈提供了强大的功能。Action的所有切面功能都有拦截器来实现(比如:取得request请求参数、验证处理等),这样你就可以用拦截器栈来组织拦截器的执行顺序。例如:你需要在使用request请求参数来设置Action属性之前,使用IoC框架设置Action的属性,反之已然。这时,你就可以为packageAction指定一个拦截器栈来实现。

 这样一个表格的对比,在网上四处可见,对此我没什么好说的,再说我也不了解struts1.1,不敢对此枉加评论。
在webwork2.2以后,webwork与struts的关系更紧密了,因为struts 2.0就在webwork2.2的基础上开发,后面两个项目会合并成一个项目,也就是说以后只有struts 2.0 没有webwork了。毕竟struts为大多数开发人员说接受,再吸收webwork的成功之处后,完全可以相信struts会越来越棒。
          所有的web容器所做的工作无非是在我们的http请求前后加上一些处理逻辑,除此之外,他们什么也不能做,注意这并不是说web容器只是很简单的做了一些操作的意思。从http请求的整个声明周期来说,web容器框架确实只是做了那么点事情。通常web容器框架的做法时,提供一些接口让用户的程序实现,这样才能达到容器框架在我们的http请求前后做手脚,webwork自然也吧例外。webwork通过FilterDispatcher来实现这一功能,用户通过实现这一接口,所有对action的请求就转化为对webwork的请求了,一切的一切都交给webwork来处理了。xwork实现了一个ActionContext,这个类封装了绝大部分web请求的相关内容,webwork的ServletAction通过继承ActionContext很好的实现了对web请求的屏蔽,用户基本上感觉不到这是一个web请求,从而实现web不存在的效果,让用户觉得写一个c/s程序和b/s程序基本上是一样,体会不到二者的差别。这是webwork的比较值得称道的地方。
         Interceptor应该是webwork第二个比较让人耳目一新的地方了,我们对任何action的请求都会在前后被Interceptor截取,ServeletDispatcher只有通过Interceptor这一层才能真正道道action,你可以想象,通过这么一个Interceptor,可以给于程序员无限的发挥空间(当然我想强调的是Interceptor功能的强大,而不是非要你去Interceptor里大做手脚)。webwork本身提供了20多个内置的Interceptor,通过这些Interceptor,我们可以实现绝大部分我们需要的功能。当然,我们也可以定义自己的Interceptor,只要实现com.opensymphony.xwork.interceptor.Interceptor接口,你就可以为所欲为了(我始终认为,程序员与各种框架之间很多时候是一个控制与反控制的关系,在利用框架带给我们便利的同时,我们也很希望它能给于我们足够的空间,让我们自己去发挥,如果说因为运用一个框架而导致我们在很多地方很别扭,一些原本很简单的东西变得复杂了,我的做法就是:一脚把这见鬼的框架踢开)。在xwork.xml里加上对次Interceptor的所用和引用的地方,你的Interceptor很快就能发挥功效了。
        IOC自然也是一个很重要的东西,但并不想在此叙述,等到介绍spring的时候再做详细介绍。
        剩下的还需要介绍的就是FreeMarker了,关于其他试图,在此不做介绍。其实FreeMarker跟标准HTML很相似,不同的地方就是可以在ftl页面上直接运用webwork自身的标签,这样能给我们带来一些便利,ftl页面如下:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head id="Head1"><meta http-equiv="Pragma" content="no-cache" />
<title></title>
<link href="Skins/default/css/common.css" rel="stylesheet" type="text/css" />
<link href="Skins/default/css/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="js/common.js"></script>
<script type="text/javascript" src="js/cachet.js"></script>
<script type="text/javascript" src="js/prototype.js"></script>
<script>
function ViewNewSeal(picd, ResoftSignCtl){
 if (trim(picd) != ""){
  return ViewSeal(picd, ResoftSignCtl);
 }
 else {
  return false;
 }
}

function CreateNewSeal(form){
 if(trim(document.all.name.value).length==0){
  alert("公章名称不能为空!!");
  return false;
 }
 return false;
}

</script>
</head>
<body>
    <@ww.form action="cachetUpdate" onsubmit="return CreateNewSeal(this)"  method="post" validate="false" enctype="multipart/form-data">

    <div  class="location"><span id="lblTitle">公章信息管理</span></div>
        <div id=main>
    <table id="tblTools" width="99%" border="0" cellpadding="0" cellspacing="0" class="toolbar">
            <tr>
                 <td align="left">
                    <input type="submit" name="btnOK" value="保 存" class="input_button" />
                    <input type="button" name="btnView" value="浏览公章" onclick="ShowSealOnSelected('${cachet.id}')" class="input_button" />
                    <input type="button" name="btnMPassword" value="修改公章密码" onclick="showModPassword()" class="input_button" />
                    <input type="button" name="btnCancel" value="取 消" onclick="location.href='cachetList.action'" class="input_button" />
     </td>
            </tr>
        </table>
         
 <div class=editblock>
 <input type="hidden" name="uploadToken" value="${uploadToken?if_exists}"/>
 <@ww.hidden name="cachet.id"/><@ww.hidden name="cachet.deptId"/>
 
 <table border="0" cellpadding="0" cellspacing="0">
  <tr>
   <th>名称:</th>
   <td><@ww.textfield name="cachet.name" cssClass="input_text" cssStyle="width:355px;"/>
     <span id="ctl02_notNullValidName" style="color:Red;display:none;"></span></td> 
  </tr>
  <tr>
   <th>描述:</th>
   <td><@ww.textfield name="cachet.description" cssClass="input_text" cssStyle="width:355px;"/></td>
  </tr>
  <tr>
   <th>状态:</th>
   <td><@ww.select name="cachet.status" list="cachet.statusMaps" value="cachet.status"/></td>
  </tr>
</@ww.form>

 <tr>
 <th>预览:</th>
 <td>
  <object id="ResoftSignCtl" classid="CLSID:95032F2D-A96E-49AF-A9B4-7F33A99AA817" width="202" height="202"></object>
 </td>
</tr>
</table>
<div id="ctl02_validaitonSummary" style="color:Red;display:none;">

/div>
</div></div>

</body>
</html>

从代码上来看 ,与标准HTML不同的地方就在于那些一"ww."开头的标签。而这些以“ww.”开头的标签最终也是被解析成标准html标签,最终用户根本识别不出来,结合webwork标签本身的灵活性,可以给我们的变成带来很大的灵活性,这种标签也是可以自己定义扩展的,只要遵照接口来就行了。这样天地够广了吧!
        关于webwork的介绍就先到这吧。
          本文参照了:moxie的《webwork教程——0.9版》、《webwork2 中文文档》、《webwork2 doc》,在此一并对原作者表示感谢。




























posted @ 2007-08-25 15:51  呀呀个呸  阅读(452)  评论(0)    收藏  举报