Action   类:
   Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
   Struts   2   Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。
线程模式:
   Struts1   Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1   Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
   Struts2   Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
Servlet   依赖:
   Struts1   Action   依赖于Servlet   API   ,因为当一个Action被调用时HttpServletRequest   和   HttpServletResponse   被传递给execute方法。
   Struts   2   Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2   Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest   和   HttpServletResponse的必要性。
可测性:
   测试Struts1   Action的一个主要问题是execute方法暴露了servlet   API(这使得测试要依赖于容器)。一个第三方扩展--Struts   TestCase--提供了一套Struts1的模拟对象(来进行测试)。
   Struts   2   Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
捕获输入:
   Struts1   使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的javabean)。
   Struts   2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过   web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种   ModelDriven   特性简化了taglib对POJO输入对象的引用。
表达式语言:
   Struts1   整合了JSTL,因此使用JSTL   EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
   Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言-- "Object   Graph   Notation   Language "   (OGNL).
绑定值到页面(view):
   Struts   1使用标准JSP机制把对象绑定到页面中来访问。
   Struts   2   使用   "ValueStack "技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
类型转换:
   Struts   1   ActionForm   属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
   Struts2   使用OGNL进行类型转换。提供基本和常用对象的转换器。
校验:
   Struts   1支持在ActionForm的validate方法中手动校验,或者通过Commons   Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
   Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
Action执行的控制:
   Struts1支持每一个模块有单独的Request   Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
   Struts2支持通过拦截器堆栈(Interceptor   Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
Struts2 Struts1.x 的深度比较
 
Struts 作为 MVC 2 的 Web 框架,自推出以来不断受到开发者的追捧,得到广泛的应用。作为最成功的 Web 框架, Struts 自然拥有众多的优点: MVC 2 模型的使用、功能齐全的标志库( Tag Library )、开放源代码。
但是,正所谓 “ 没有最好,只有更好 ” , Struts1.x 自身也有不少的缺点:需要编写的代码过多,容易引起 “ 类爆炸 ” 、单元测试困难。这些缺点随着 Web 的发展越来越明显。这就促生了 Struts 2 ,它的诞生能很好的解决上述问题。
在本文中,笔者将对 Struts2 和 Struts1.x 这两种框架进行详细的比较。比较将涉及到这两种框架的 Action 、验证、类型转换及如何开发等方面的内容。希望通过这样的比较,让读者了解这两种框架各自的特点,以便于在自己的项目中,根据实际情况,尽快的过渡到 Struts2 的时代。本文的内容基于 Struts<?XML:NAMESPACE PREFIX = ST1 />2.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 所示。
拦截器调用序列器
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 一起使用。