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