Struts2与Struts1.x的深度比较

【IT168 专稿】Struts 作为 MVC 2 Web 框架,自推出以来不断受到开发者的追捧,得到广泛的应用。作为最成功的 Web 框架, Struts 自然拥有众多的优点: MVC 2 模型的使用、功能齐全的标签库( Tag Library )、开放源代码。
但是,正所谓 没有最好,只有更好 Struts1.x 自身也有不少的缺点:需要编写的代码过多,容易引起 类爆炸 、单元测试困难。这些缺点随着 Web 的发展越来越明显。这就促生了 Struts 2 ,它的诞生能很好的解决上述问题。
在本文中,笔者将对 Struts2 Struts1.x 这两种框架进行详细的比较。比较将涉及到这两种框架的 Action 、验证、类型转换及如何开发等方面的内容。希望通过这样的比较,让读者了解这两种框架各自的特点,以便于在自己的项目中,根据实际情况,尽快的过渡到 Struts2 的时代。本文的内容基于 Struts2.0.6
一、 引言
Struts 的第一个版本是在 2001 5 月份发布的。它的最初设想是通过结合 JSP Servlet ,使 Web 应用的视图和业务 / 应用逻辑得以清晰地分离开来。在 Struts 之前,最常见的做法是在 JSP 中加入业务和应用逻辑,或者在 Servlet 中通过 println() 来生成视图。
自从第一版发布以来, Struts 实际上已成为业界公认的 Web 应用标准。它的炙手可热也为自己带来了改进和变更,所以不但要跟上对 Web 应用框架不断变化的需求,而且要与日渐增多竞争激烈的众多框架的特性相融合。
到最后,产生了几个下一代 Struts 的解决方案。其中两个最受瞩目的方案是 Shale Struts Ti Shale 是一个基于构件的框架,并在最近成为 Apache 的顶级项目。而 Struts Ti 则是在 Struts 的成功经验基础上继续坚持对前端控制器( Front Controller )和 MVC model-view-controller )模式进行改进。
WebWork 项目是在 2002 3 月发布的,它对 Struts 式框架进行了革命性改进,引进了不少新的思想、概念和功能,但和原 Struts 代码并不兼容。 WebWork 是一个成熟的框架,经过了好几次重大的改进与发布。
2005 12 月, WebWork Struts Ti 宣布合并。与此同时, Struts Ti 改名为 Struts Action Framework 2.0 ,成为 Struts 真正的继承者。
最后要注意的是,并不是说 Struts WebWork 项目已经停止开发了。由于人们对这两个项目的兴趣仍然很高,而且也有很多开发者仍然愿意使用它们,因此这两个项目还在继续开发中,继续修复 Bug ,改进功能和继续添加新功能。

二、 Action 的区别
对于有着丰富的 Struts1.x 开发经验的朋友来说,都十分的清楚 Action 是整个 Struts 框架的核心内容,当然 Struts2 也不例外。不过, Struts1.x Struts2 Action 模型很大的区别。
Struts2和Struts1.x的差别,最明显的 就是Struts2是一个pull-MVC架构。这是什么意思呢?从开发者角度看,就是说需要显示给用户的数据可以直接从Action中获取,而不像 Struts1.x那样,必须把相应的Bean存到Page、Request或者Session中才能获取。 Struts1.x 必须继承 org.apache.struts.action.Action 或者其子类,表单数据封装在 FormBean 中。 Struts 2 无须继承任何类型或实现任何接口,表单数据包含在 Action 中,通过 Getter Setter 获取(如下面的 ActionForStruts2 的代码示例)。
虽然,在理论上 Struts2 Action 无须实现任何接口或者是继承任何的类,但是,在实际编程过程中,为了更加方便的实现 Action ,大多数情况下都会继承 com.opensymphony.xwork2.ActionSupport 类,并且重载( Override )此类里的 String execute() 方法。如下所示:
    
    
package ActionDiffer;
import java.text.DateFormat;
import java.util.Date;

import com.opensymphony.xwork2.ActionSupport;

public class ActionForStruts2 extends ActionSupport ... {
private String message;

public String getMessage() ...{
return message;
}

@Override
public String execute() ...{
message
= " This is hello from strtuts2. Now is: " + DateFormat.getInstance().format( new Date());
return SUCCESS;
}
}

首先,从 ActionForStruts2 可以看出,返回的对象不是 ActionForward ,而是 String 。如果你不喜欢以字符串的形式出现在你的代码中,有个 Helper 接口 Action 可以以常量方式提供常见结果,如 “success” “none” “error” “input” “login”
另外, 按照惯例,在 Struts1.x 中只有 “execute” 方法能调用 Action,  但在 Struts2 中并非必要,任何声明为 public String methodName() 方法,都能通过配置来调用 Action
最后,和 Struts1.x 最大的革命性的不同是, Struts2 处理 Action 过程中调用的方法( “execute” 方法)是不带参数的。那如何获取所需要的对象呢?答案是使用 IoC (反转控制, Inversion of Control) ,也叫 依赖注入( Dependency Injection 的模式(想更多地了解这方面信息请看 Martin Fowler 的文章 http://www.martinfowler.com/articles/injection.html )。 Spring 框架使得这个模式流行起来,然而 Struts2 的前身( WebWork )也同时应用上了这个模式。

三、 IoC
IoC(Inversion of Control ,以下译为控制反转),随着 Java 社区中轻量级容器( Lightweight Contianer )的推广而越来越为大家耳熟能详。在此,无需再多费唇舌来解释 什么是控制反转 为什么需要控制反转 。因为互联网上已经有非常多的文章对诸如此类的问题作了精彩而准确的回答。读者可以去读一下 Rod Johnson Juergen Hoeller 合著的《 Expert one-on-one J2EE Development without EJB 》或 Martin Fowler 所写的《 Inversion of Control Containers and the Dependency Injection pattern 》。
众所周知, Struts2 是以 Webwork 2 作为基础发展出来。而在 Webwork 2.2 之前的 Webwork 版本,其自身有一套控制反转的实现, Webwork 2.2 Spring 框架 的如火如荼发展的背景下,决定放弃控制反转功能的开发,转由 Spring 实现。值得一提的是, Spring 确实是一个值得学习的框架,因为有越来越多的开源组件(如 iBATIS 等)都放弃与 Spring 重叠的功能的开发。因此, Struts2 推荐大家通过 Spring 实现控制反转。
为了更好地了解反转控制,下面来看一个例子,如何利用 IoC Action 处理过程中可以访问到当前请求 HttpServerRequest 对象。
在例子中,使用的依赖注入机制是接口注入。就如其名称一样,接口注入需要的是已经被实现了的接口。这个接口包含了相应属性的 setter ,为 Action 提供值。例子中使用了 ServletRequestAware 接口,如下:
public interface ServletRequestAware {
public void setServletRequest(HttpServletRequest request);
}
当继承这个接口后,原本简单的 Action 看起来有点复杂了,但是这时可以获取 HttpServerRequest 对象来使用了。
public class IoCForStruts2 implements ServletRequestAware {
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String execute() throws Exception {
//
可以开始使用 request 对象进行工作了
return Action.SUCCESS;
}
}
看起来现在这些属性是类级别的,并不是线程安全的,会出现问题。其实在 Struts2 里并没有问题,因为每个请求过来的时候都会产生一个新的 Action 对象实例,它并没有和其他请求共享一个对象,所以不需要考虑线程安全问题。

四、 拦截器
Interceptor (以下译为拦截器),在 AOP Aspect-Oriented Programming )中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是 AOP 的一种实现策略。
Webwork 的中文文档的解释为 —— 拦截器是动态拦截 Action 调用的对象。它提供了一种机制可以使开发者定义在一个 action 执行的前后执行的代码,也可以在一个 action 执行前阻止其执行。同时也提供了一种可以提取 action 中可重用的部分的方式。
Struts1.x 的标准框架中不提供任何形式的拦截器,虽一个名为 SAIF 的附加项目则实现了这样的功能,但它的适用的范围还很有限。
拦截器是 Struts2 的一个强有力的工具,有许多功能( feature )都是构建于它之上,如 国际化 转换器 校验 等。谈到拦截器,还有一个流行的词 —— 拦截器链( Interceptor Chain ,在 Struts2 中称为拦截器栈 Interceptor Stack )。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
Struts 2 的拦截器实现相对比较简单。当请求到达 Struts2 ServletDispatcher 时, Struts 2 会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表( list ),最后一一地调用列表中的拦截器,如图 1 所示。

Struts2与Struts1.x的深度比较

Struts 2 已经提供丰富多样功能齐全的拦截器实现。读者可以到 struts2-all-2.0.6.jar struts2-core-2.0.6.jar 包的 struts-default.xml 查看关于默认的拦截器与拦截器链的配置。
作为 框架( framework ,可扩展性是不可缺少的,因为世上没有放之四海而皆准的东西。虽然, Struts 2 为我们提供如此丰富的拦截器实现,但是这并不意味我们失去创建自定义拦截器的能力,恰恰相反,在 Struts 2 自定义拦截器是相当容易的一件事。

五、 Struts2 Struts1.x 的全面比较

为了对 Struts2 Strtus1.x 进行全面的比较,让读者了解这两种框架各自的优缺点,以便于在自己的项目中,根据实际情况,选择合适的框架,对它们两者进行比较,总结了如下表分析比较。

特性
Struts1.x
Struts2
Action
Struts1.x要求Action类要扩展自一个抽象基类。Struts1.x的一个共有的问题是面向抽象类编程而不是面向接口编程。
Struts2 的Action类实现了一个Action接口,连同其他接口一起来实现可选择和自定义的服务。Struts2提供一个名叫ActionSupport的基 类来实现一般使用的接口。当然,Action接口不是必须的。任何使用execute方法的POJO对象可以被当作Struts 2的Action对象来使用。
线程模型
Struts1.x Action类是单例类,因为只有一个实例来控制所有的请求。单例类策略造成了一定的限制,并且给开发带来了额外的烦恼。Action资源必须是线程安全或者同步的。
Struts2 Action对象为每一个请求都实例化对象,所以没有线程安全的问题。(实践中,servlet容器给每一个请求产生许多丟弃的对象,并且不会导致性能和垃圾回收问题)。
Servlet 依赖
Struts1.x的Action类依赖于servlet API,当Action被调用时,以HttpServletRequest和HttpServletResponse作为参数传给execute方法。
Struts2 的Action和容器无关。Servlet上下文被表现为简单的Maps,允许Action被独立的测试。Struts2的Action可以访问最初的请 求(如果需要的话)。但是,尽可能避免或排除其他元素直接访问HttpServletRequest或HttpServletResponse。
易测性
测试Struts1.x的主要问题是execute方法暴露了Servlet API这使得测试要依赖于容器)。第三方的扩展,如Struts TestCase,提供了一套Struts1的模拟对象(来进行测试)。
Struts2的Action可以通过初始化、设置属性、调用方法来测试。依赖注入的支持也是测试变得更简单。
捕获输入
Struts1.x 使用ActionForm对象来捕获输入。象Action一样,所有的ActionForm必须扩展基类。因为其他的JavaBean不能作为 ActionForm使用,开发者经常创建多余的类来捕获输入。DynaBeans可以被用来作为替代ActionForm的类来创建。但是,开发者可能 是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的javabean)。
Struts2 直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。
表达式语言
Struts1.x整合JSTL,所以它使用JSTL的表达式语言。表达式语言有基本的图形对象移动,但是对集合和索引属性的支持很弱。
Struts2使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL)。
将值绑定到页面
Struts1.x使用标准JSP机制来绑定对象到页面上下文。
Struts2使用“ValueStack”技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
类型转换
Struts1.x的ActionForm属性经常都是String。Struts 1.x使用Commons-Beanutils来进行类型转换。转换每一个类,而不是为每一个实例配置。
Struts2使用OGNL进行类型转换。提供基本和常用对象的转换器。
验证
Struts1.x支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
Action 执行控制
Struts1.x支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
六、 结论

前面已经简要介绍了 Struts2的起源,并详细对比了Struts2和Struts1.x的差异,读者应该对Struts2的基础有所了解了——包括高层的框架概念和基础 的请求流程,并理解Struts1.x和Struts2两者之间在Action方面的差别,Struts2加强了对拦截器与IoC的支持,而在 Struts1.x中,这些特性是很难想象的。
同时,读者应该明白: Struts2是WebWork的升级,而不是Struts 1.x的升级。虽然Struts 2提供了与Struts1.x的兼容,但已经不是Struts1.x的升级。对于已有Struts1.x开发经验的开发者而言,Struts1.x的开发 经验对于Struts2并没有太大的帮助;相反,对于已经有WebWork开发经验的开发者而言,WebWork的开发经验对Struts2的开发将有很 好的借鉴意义。

作者: IT168 自由紫风 2007-07-13
http://tech.it168.com/oldarticle/2007-07-13/200707131009859_4.shtml

你可能感兴趣的:(spring,框架,应用服务器,struts,Webwork)