《.NET程序员开始学JAVA》之 webwork 篇
在接触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编程中会引来一些问题,就是关于多种继承的问题。 |
WebWork的Action类仅需要实现接口com.opensymphony.xwork.Action,也可以实现其它的接口来实现更多的功能,譬如:validate(验证),localware(国际化)等。当然,它也提供了一个类ActionSupport集成了上面的所有功能,我们在开发中可以根据需要选择。 |
|
线程模型 |
Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。 |
在WebWork中,每个请求对应一个Action,因此没有线程的安全问题。实际上Servlet容器对每个请求也产生多个对象,它也没有证明对性能和垃圾回收产生太多的影响。 |
|
Servlet的依赖 |
Struts处理Action时必需要依赖ServletRequest 和ServletResponse,所有它摆脱不了Servlet容器。 |
WebWork的Action不用依赖Web层和其它的容器。它可以通过ActionContext,直接去访问Request和Response,但这个是可选的,只有在必需的请求下使用。 |
|
测试 |
Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。 |
Webwork的action能够通过赋予一定的属性,就可以执行单元测试。同时也可以使用一个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,但它同时支持:velocity、freemaker、jspparer、xml。 |
|
类型的转换 |
Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。 |
WebWork使用OGNL进行类型转化,提供了所有基本类型的转化功能。类型转化可以直接对一个Class进行(Class级别)转化,也可以对Class的字段进行类型转化。它使用拦截器可以很容易的将类型转化的错误信息返回给用户,而且错误信息可以对应到一个相应的字段。 |
|
对Action 执行前和后的处理 |
Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。 |
Webwork2 允许您处理Action可以通过拦截器,就是在每一个Action处理前或者后进行其它操作。它的拦截器可以在配置文件中动态添加,这样Action和拦截器之间完全解藕,更好的实现了组件化。 |
|
验证处理 |
Struts的验证是调用FormBean的validator()方法,其实就是对FormBean的验证。它一般使用框架Commons Validation进行数据验证处理。它使用了一个全局的配置文件validation.xml定义了FormBean的验证信息。Struts的FormBean属性都被认为是String类型,所以它在验证时也需要额外的类型转化。 |
WebWork使用Xwork的验证框架进行验证处理,它可以通过配置拦截器来激活。它可以为每个需要验证的Class指定一个xml验证文件,也可以为一个Class在不同的情况指定不同的xml验证文件。WebWork证可以给每个Action类指定对应的验证文件,也可以给Action的字段去指定验证文件。通过拦截器来组装Action和其验证文件,使它们之间完全解藕。 |
|
对Action执行的控制 |
Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。 |
在这个方面,WebWork的拦截器栈提供了强大的功能。Action的所有切面功能都有拦截器来实现(比如:取得request请求参数、验证处理等),这样你就可以用拦截器栈来组织拦截器的执行顺序。例如:你需要在使用request请求参数来设置Action属性之前,使用IoC框架设置Action的属性,反之已然。这时,你就可以为package或Action指定一个拦截器栈来实现。 |
在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》,在此一并对原作者表示感谢。

浙公网安备 33010602011771号