Struts做为MVC 2的Web框架,自推出以来不断受到开发者的追捧,获得普遍的应用。做为最成功的Web框架,Struts天然拥有众多的优势:MVC 2模型的使用、功能齐全的标志库(Tag Library)、开放源代码。
可是,正所谓“没有最好,只有更好”,Struts1.x自身也有很多的缺点:须要编写的代码过多,容易引发“类爆炸”、单元测试困难。这些缺点随着Web的发展愈来愈明显。这就促生了Struts 2,它的诞生能很好的解决上述问题。
在本文中,笔者将对Struts2和Struts1.x这两种框架进行详细的比较。比较将涉及到这两种框架的Action、验证、类型转换及如何开发等方面的内容。但愿经过这样的比较,让读者了解这两种框架各自的特色,以便于在本身的项目中,根据实际状况,尽快的过渡到Struts2的时代。本文的内容基于Struts2.0.6。
1、引言
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,改进功能和继续添加新功能。
2、 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的文章[url]http://www.martinfowler.com/articles/injection.html[/url])。Spring框架使得这个模式流行起来,然而Struts2的前身(WebWork)也同时应用上了这个模式。
3、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对象实例,它并无和其余请求共享一个对象,因此不须要考虑线程安全问题。
4、拦截器
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),最后一一地调用列表中的拦截器。
Struts 2已经提供丰富多样功能齐全的拦截器实现。读者能够到struts2-all-2.0.6.jar或struts2-core-2.0.6.jar包的struts-default.xml查看关于默认的拦截器与拦截器链的配置。
做为“框架(framework)”,可扩展性是不可缺乏的,由于世上没有放之四海而皆准的东西。虽然,Struts 2为咱们提供如此丰富的拦截器实现,可是这并不意味咱们失去建立自定义拦截器的能力,偏偏相反,在Struts 2自定义拦截器是至关容易的一件事。
5、Struts2和Struts1.x的全面比较
为了对Struts2和Strtus1.x进行全面的比较,让读者了解这两种框架各自的优缺点,以便于在本身的项目中,根据实际状况,选择合适的框架,对它们二者进行比较,总结了以下表分析比较。
特性比较:
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一块儿使用。
6、结论
前面已经简要介绍了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的开发将有很好的借鉴意义