WebWork简介

WebWork虽然没有Struts 1那样赫赫有名,但也是出身名门,WebWork来自另外一个优秀的开源组织:opensymphony,这个优秀的开源组织同样开发了大量优秀的开源项目,如Qutarz、OSWorkFlow等。实际上,WebWork的创始人则是另一个Java领域的名人:Rickard Oberg(他就是JBoss和XDoclet的作者)。

相对于Struts 1存在的那些先天性不足而言,WebWork则更加优秀,它采用了一种更加松耦合的设计,让系统的Action不再与Servlet API耦合。使单元测试更加方便,允许系统从B/S结构向C/S结构转换。

相对于Struts 1仅支持JSP表现层技术的缺陷而言,WebWork支持更多的表现层技术,如Velocity、FreeMarker和XSLT等。

WebWork可以脱离Web应用使用,这一点似乎并没有太多优势,因为,一个应用通常开始已经确定在怎样的环境下使用。WebWork有自己的控制反转(Inversion of Control)容器,通过控制反转,可以让测试变得更简单,测试中设置实现服务接口的Mock对象完成测试,而不需要设置服务注册。

WebWork 2使用OGNL这个强大的表达式语言,可以访问值栈。OGNL对集合和索引属性的支持非常强大。

WebWork建立在XWork之上,使用ServletDispatcher作为该框架的核心控制器,处理HTTP的响应和请求。

从处理流程上来看,WebWork与Struts 1非常类似,它们的核心都由控制器组成,其中控制器都由两个部分组成:

— 核心控制器ServletDispatcher,该控制器框架提供。

— 业务逻辑控制器Action,该控制器由程序员提供。

相对Struts 1的Action与Servlet API紧紧耦合的弱点来说,WebWork的Action则完全与Servlet API分离,因而该Action更容易测试。

WebWork的Action可以与Servlet API分离,得益于它灵巧的设计,它使用一个拦截器链,负责将用户请求数据转发到Action,并负责将Action的处理结果转换成对用户的响应。

当用户向Web应用发送请求时,该请求经过ActionContextCleanUp、SiteMesh等过滤器过滤,由WebWork的核心控制器拦截,如果用户请求需要WebWork的业务逻辑控制器处理,该控制器则调用Action映射器,该映射器将用户请求转发到对应的业务逻辑控制器。值得注意的是,此时的业务逻辑控制器并不是开发者实现的控制器,而是WebWork创建的控制器代理。

创建控制器代理时,WebWork需要得到开发者定义的xwork.xml配置文件,控制器代理以用户实现的控制器作为目标,以拦截器链中的拦截器作为处理(Advice)。

提示 WebWork中创建控制器代理的方式,就是一种AOP(面向切面编程)编程方式,只是这种AOP中的拦截器由系统提供,因此无需用户参与。如果读者需要获取更多关于AOP编程的知识,请参阅AOP相关资料,或笔者所著的《Spring 2.0宝典》一书的第6章。

开发者自己实现的业务逻辑控制器只是WebWork业务控制器的目标——这就是为什么开发者自己实现的Action可以与Servlet API分离的原因。当开发者自己的Action处理完HTTP请求后,该结果只是一个普通字符串,该字符串将对应到指定的视图资源。

指定的试图资源经过拦截器链的处理后,生成对客户端的响应输出。

上面整个过程的数据流图如下图所示。

与前面的Struts 1框架对比,不难发现WebWork在很多地方确实更优秀。相对Struts 1的种种缺点而言,WebWork存在如下优点:

(1)Action无需与Servlet API耦合,更容易测试

相对于Struts 1框架中的Action出现了大量Servlet API而言,WebWork的Action更像一个普通Java对象,该控制器代码中没有耦合任何Servlet API。看下面的WebWork的Action示例:[code]publicclassLoginActionimplementsAction

{

//该字符串常量将作为Action的返回值

privatefinalstaticStringLOGINFAIL="loginfail";

//该Action封装的两个请求参数

privateStringpassword;

privateStringusername;

//password请求参数对应的getter方法

publicStringgetPassword()

{

	returnpassword;

}

//password请求参数对应的setter方法

publicvoidsetPassword(Stringpassword)

{

	this.password=password;

}

//username请求参数对应的getter方法

publicStringgetUsername()

{

	returnusername;

}

//username请求参数对应的setter方法

publicvoidsetUsername(Stringusername)

{

	this.username=username;

}

//处理用户请求的execute方法

publicStringexecute()throwsException

{

	if("yeeku".equalsIgnoreCase(getUsername())

			&&"password".equals(getPassword()))

	{

		ActionContextctx=ActionContext.getContext();

		//将当前登录的用户名保存到Session

		Mapsession=ctx.getSession();

		session.put("username",getUsername());

		returnSUCCESS;

	}

	else

	{

		returnLOGINFAIL;

	}

}

}[/code]在上面的Action代码中,我们看不到任何的Servlet API,当系统需要处理两个请求参数:username和password时,Action并未通过HttpServletRequest对象来获得请求参数,而是直接调用访问该Action的username和password成员属性——这两个属性由Action拦截器负责初始化,以用户请求参数为其赋值。

即使Action中需要访问HTTP Session对象,依然没有在代码中直接出现HttpSession API,而是以一个Map对象代表了HTTP Session对象。

当我们将WebWork的Action和Struts 1的Action进行对比时,不难发现Struts 1的Action确实太臃肿了,确实不如WebWork的Action那么优雅。

如果需要测试上面的Action代码,测试用例的书写将非常容易,因为execute方法中没有包含任何Servlet API,甚至没有WebWork的API。

(2)Action无需与WebWork耦合,代码重用率高

在上面的Action代码中,不难发现WebWork中的Action其实就是一个POJO,该Action仅仅实现了WebWork的Action接口,包含了一个execute方法。

Struts 1中的Action类需要继承Struts 1的Action类。我们知道,实现一个接口和继承一个类是完全不同的概念:实现一个接口对类的污染要小得多,该类也可以实现其他任意接口,还可以继承一个父类;但一旦已经继承一个父类,则意味着该类不能再继承其他父类。

除此之外,Struts 1中Action也包含了一个execute方法,但该方法需要4个参数,类型分别是ActionMapping、ActionForm、HttpServletRequest和HttpServletResponse,一个包含了这4个参数的方法,除了在Struts 1框架下有用外,笔者难以想象出该代码还有任何复用价值。但WebWork的execute方法则完全不同,该方法中没有出现任何Servlet API,也没有出现任何WebWork API,这个方法在任何环境下都有重用的价值。

得益于WebWork灵巧的设计,WebWork中的Action无需与任何Servlet API、WebWork API耦合,从而具有更好的代码重用率。

(3)支持更多的表现层技术,有更好的适应性

正如从上图所见到的,WebWork对多种表现层技术:JSP、Velocity和FreeMarker等都有很好的支持,从而给开发更多的选择,提供了更好的适应性。