1.2 Web应用的发展
1.2.1 Web技术的发展
随着Internet技术的广泛使用,Web技术已经广泛应用于Internet,但早期的Web应用全部是静态的HTML页面,用于将一些文本信息呈现给浏览者,但这些信息是固定写在HTML页面里的,该页面不具备与用户交互的能力,没有动态显示的功能。
于是,人们希望Web应用里包含一些能动态执行的页面,最早的CGI(通用网关接口)技术满足了该要求,CGI技术使得Web应用可以与客户端浏览器交互,不再需要使用静态的HTML页面。CGI技术可以从数据库中读取信息,将这些信息呈现给用户;还可以获取用户的请求参数,并将这些参数保存到数据库里。
CGI技术开启了动态Web应用的时代,给予了这种技术无限的可能性。但CGI技术存在很多缺点,其中最大的缺点就是开发动态Web应用难度非常大,而且在性能等各方面也存在限制。到1997年,随着Java语言的广泛使用,Servlet技术迅速成为动态Web应用的主要开发技术。与传统的CGI应用相比,Servlet具有大量的优势:
(1)Servlet是基于Java语言创建的,而Java语言则内建了多线程支持,这一点大大提高了动态Web应用的性能。
(2)Servlet应用可以充分利用Java语言的优势,如JDBC(Java DataBase Connection)等。同时,Java语言提供了丰富的类库,这些都简化了Servlet的开发。
(3)除此之外,Servlet运行在Web服务器中,由Web服务器去负责管理Servlet的实例化,并对客户端提供多线程、网络通信等功能,这都保证Servlet有更好的稳定性和性能。
Servlet在Web应用中被映射成一个URL(统一资源定位),该URL可以被客户端浏览器请求,当用户向指定URL对应的Servlet发送请求时,该请求被Web服务器接收到,该Web服务器负责处理多线程、网络通信等功能,而Servlet的内容则决定了服务器对客户端的响应内容。
图1-3所示为Servlet的响应流程,浏览器向Web服务器内指定的Servlet发送请求,Web服务器根据Servlet生成对客户端的响应。
图1-3 Servlet的响应流程
实际上,这是后来所有的动态Web编程技术所使用的模型,这种模型都需要一个动态的程序或一个动态页面,当客户端向该动态程序或动态页面发送请求时,Web服务器根据该动态程序来生成对客户端的响应。
到了1998年,微软发布了ASP 2.0,它是Windows NT 4 Option Pack的一部分,作为IIS 4.0的外接式附件。它与ASP 1.0的主要区别在于它的外部组件是可以初始化的,这样,在ASP程序内部的所有组件都有了独立的内存空间,并可以进行事务处理。这标志着ASP技术开始真正作为动态Web编程技术。
当ASP技术在世界上广泛流行时,人们很快感受到这种简单技术的魅力:ASP使用VBScript作为脚本语言,它的语法简单、开发效率非常高。而且,世界上已经有了非常多的VB程序员,这些VB程序员可以很轻易地过渡成ASP程序员。因此,ASP技术马上成为应用最广泛的动态Web开发技术。
随后,由Sun带领的Java阵营,立即发布了JSP标准,从某种程度上来看,JSP是Java阵营为了对抗ASP推出的一种动态Web编程技术。
ASP和JSP从名称上如此相似,但它们的运行机制存在一些差别,这主要是因为VBScript是一种脚本语言,无需编译,而JSP使用Java作为脚本语句,但Java从来就不是解释型的脚本语言,因此JSP页面并不能立即执行。因此,JSP必须编译成Servlet,也就是说:JSP的实质还是Servlet。不过,书写JSP比书写Servlet简单得多。
JSP的运行机理如图1-4所示。
图1-4 JSP的运行机理
对比图1-3和图1-4,发现无论是Servlet动态Web技术,还是JSP动态Web技术,它们的实质完全一样。可以这样理解:JSP是一种更简单的Servlet技术,这也是JSP技术出现的意义——作为一个和ASP对抗的技术,简单就是JSP的最大优势。
随着实际Web应用的使用越来越广泛,Web应用的规模也越来越大,开发人员发现动态Web应用的维护成本越来越大,即使只需要修改该页面的一个简单按钮文本或一段静态的文本内容,也不得不打开混杂的动态脚本的页面源文件进行修改,这是一种很大的风险,完全有可能引入新的错误。
这个时候,人们意识到,使用单纯的ASP或JSP页面充当过多角色是相当失败的选择,这对于后期的维护相当不利。慢慢地,开发人员开始在Web开发中使用MVC模式。
随后Java阵营发布了一套完整的企业开发规范:J2EE(现在已经更名为Java EE),紧接着,微软也发布了ASP.NET技术,它们都采用一种优秀的分层思想,力图解决Web应用维护困难的问题。动态Web编程技术的发展历史如图1-5所示。
图1-5 动态Web编程技术的发展历史
1.2.2 Model 1和Model 2
Java阵营的动态Web编程技术经历了所谓的Model 1和Model 2时代。
Model 1就是JSP大行其道的时代,在Model 1模式下,整个Web应用几乎全部由JSP页面组成,JSP页面接收处理客户端请求,对请求处理后直接作出响应。用少量的JavaBean来处理数据库连接、数据库访问等操作。
图1-6所示为Model 1的程序流程。
图1-6 Model 1的程序流程
Model 1模式的实现比较简单,适用于快速开发小规模项目。但从工程化的角度看,它的局限性非常明显:JSP页面身兼View和Controller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。
早期有大量ASP和JSP技术开发出来的Web应用,这些Web应用都采用了Model 1架构。
Model 2已经是基于MVC架构的设计模式。在Model 2架构中,Servlet作为前端控制器,负责接收客户端发送的请求,在Servlet中只包含控制逻辑和简单的前端处理;然后,调用后端JavaBean来完成实际的逻辑处理;最后,转发到相应的JSP页面处理显示逻辑。其具体的实现方式如图1-7所示。
图1-7 Model 2的程序流程
从图1-7中可以看到,Model 2下JSP不再承担控制器的责任,它仅仅是表现层角色,仅仅用于将结果呈现给用户,JSP页面的请求与Servlet(控制器)交互,而Servlet负责与后台的JavaBean通信。在Model 2模式下,模型(Model)由JavaBean充当,视图(View)由JSP页面充当,而控制器(Controller)则由Servlet充当。
由于引入了MVC模式,使Model 2具有组件化的特点,更适用于大规模应用的开发,但也增加了应用开发的复杂程度。原本需要一个简单的JSP页面就能实现的应用,在Model 2中被分解成多个协同工作的部分,需花费更多的时间才能真正掌握其设计和实现过程。
注意
对于非常小型的Web站点,如果后期的更新、维护工作不是特别多,可以使用Model 1的模式来开发应用,而不是使用Model 2的模式。虽然Model 2提供了更好的可扩展性及可维护性,但增加了前期开发成本。从某种程度上讲,Model 2降低了系统后期维护的复杂度,却导致了前期开发的更高复杂度。
1.2.3 MVC
1.MVC思想
MVC并不是Java语言所特有的设计思想,也并不是Web应用所特有的思想,它是所有面向对象程序设计语言都应该遵守的规范。
MVC思想将一个应用分成3个基本部分:Model(模型)、View(视图)和Controller(控制器),这3个部分以最少的耦合协同工作,从而提高应用的可扩展性及可维护性。
最初,MVC模式是针对相同的数据需要不同显示的应用而设计的,其整体效果如图1-8所示。
图1-8 MVC结构的整体效果
在MVC模式中,事件由控制器处理,控制器根据事件的类型改变模型或视图,反之亦然。具体来说,每个模型对应一系列的视图列表,这种对应关系通常采用注册来完成,即把多个视图注册到同一个模型,当模型发生改变时,模型向所有注册过的视图发送通知,接下来,视图从对应的模型中获得信息,然后完成视图显示的更新。
从设计模式的角度来看,MVC思想非常类似于一个观察者模式,但与观察者模式存在少许差别:在观察者模式下,观察者和被观察者可以是两个互相对等的对象,但对于MVC思想而言,被观察者往往只是单纯的数据体,而观察者则是单纯的视图页面。
2.MVC模式的优势
概括起来,MVC有如下优势。
(1)多个视图可以对应一个模型。按MVC设计模式,一个模型对应多个视图,可以减少代码的复制及代码的维护量,一旦模型发生改变,也易于维护。
(2)模型返回的数据与显示逻辑分离。模型数据可以应用任何的显示技术,如使用JSP页面、Velocity模板或直接产生Excel文档等。
(3)应用被分隔为3层,降低了各层之间的耦合,提高了应用的可扩展性。
(4)控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起,完成不同的请求。因此,控制层可以说是包含了用户请求权限的概念。
(5)MVC更符合软件工程化管理的精神。不同的层各司其职,每一层的组件具有相同的特征,有利于通过工程化和工具化产生管理程序代码。
3.Web模式下的MVC
相对于早期的MVC思想,经典的MVC思想与Web应用的MVC思想也存在一定的差别,引起差别的主要原因是Web应用是一种请求/响应模式下的应用,对于请求/响应应用,如果用户不对应用发出请求,视图无法自己主动更新。
对于一个应用程序而言,我们可以将视图注册给模型,当模型数据发生改变时,即时通知视图页面发生改变;而对于Web应用而言,即使将多个JSP页面注册给一个模型,当模型发生变化时,模型也无法主动发送消息给JSP页面(因为Web应用都是基于请求/响应模式的),只有当用户请求浏览该页面时,控制器才负责调用模型数据来更新JSP页面。
1.2.4 Struts:基于MVC 的坚固框架
Struts是在MVC模式基础上构建Web应用程序的一种开放源码框架。Structs鼓励在MVC模式上构建应用程序并且提供大多数Web应用程序所共有的服务。
在Struts应用程序中,可以构建模型层,这样业务逻辑与数据检索逻辑重用就很容易了。这层负责运行应用程序的业务逻辑,获取相关数据(如运行SQL命令或读取平面文件)。
Struts鼓励在模型—视图—控制器设计范例基础上构建应用程序。Structs提供自己的控制器组件(ActionController类)并与其他技术相结合来提供模型与视图。对于模型(Model类), Struts能与任何标准的数据访问技术相结合,包括EJB、JDBC,以及Object-Relational Bridge。对于视图(ActionForm类),Struts在JSP环境及其他描述系统中运行地很好。
1.Struts 1
从过去的经验来看,Struts 1是所有MVC框架中不容辩驳的胜利者,不管是市场占有率,还是所拥有的开发人群,Struts 1都拥有其他MVC框架不可比拟的优势。Struts 1的成功得益于它丰富的文档、活跃的开发群体。当然,Struts 1是世界上第一个发布的MVC框架,Struts 1.0在2001年6月发布,这一点可能是使它得到如此广泛拥戴的主要原因。
为了使读者明白Struts 1的运行机制,下面将简要介绍Struts 1的基本框架。
Struts 1框架以ActionServlet作为核心控制器,整个应用由客户端请求驱动。当客户端向Web应用发送请求时,请求将被Struts 1的核心控制器ActionServlet拦截,ActionServlet根据请求决定是否需要调用业务逻辑控制器处理用户请求(实际上,业务逻辑控制器还是控制器,它只是负责调用模型来处理用户请求),当用户请求处理完成后,其处理结果通过JSP呈现给用户。
对于整个Struts 1框架而言,控制器就是它的核心,Struts 1的控制器由两个部分组成:核心控制器和业务逻辑控制器。其中核心控制器就是ActionServlet,由Struts 1框架提供;业务逻辑控制就是用户自定义的Action,由应用开发者提供。
大部分用户请求,都需要得到服务器的处理。当用户发送一个需要得到服务器处理的请求时,该请求被ActionServlet拦截到,ActionServlet将该请求转发给对应的业务逻辑控制器,业务逻辑控制器调用模型来处理用户请求;如果用户请求只是希望得到某个URL资源,则由ActionServlet将被请求的资源转发给用户。
Struts 1的程序运行流程如图1-9所示。
图1-9 Struts 1的程序运行流程
下面针对Struts 1程序流程具体分析MVC中的3个角色。
(1)Model部分。
Struts 1的Model部分主要由底层的业务逻辑组件充当,这些业务逻辑组件封装了底层数据库访问、业务逻辑方法实现。实际上,对于一个成熟的企业应用而言,Model部分也不是一个简单的JavaBean所能完成的,它可能是一个或多个EJB组件,可能是一个WebService服务。总之,Model部分封装了整个应用的所有业务逻辑,但整个部分并不是由Struts 1提供的,Struts 1也没有为实现Model组件提供任何支持。
(2)View部分。
Struts 1的View部分采用JSP实现。Struts 1提供了丰富的标签库,通过这些标签库可以最大限度地减少脚本的使用。这些自定义的标签库可以输出控制器的处理结果。
虽然Struts 1提供了与Ties框架的整合,但Struts 1所支持的表现层技术非常单一:既不支持FreeMarker、Velocity等模板技术,也不支持JasperReports等报表技术。
(3)Controller部分。
Struts 1的Controller部分由两个部分组成。
■ 系统核心控制器:由Struts 1框架提供,也就是系统中的ActionServlet。
■ 业务逻辑控制器:由Struts 1框架提供,也就是用户自己实现的Action实例。
Struts 1的核心控制器对应图1-9中的核心控制器(ActionServlet)。该控制器由Struts 1框架提供,继承HttpServlet类,因此可以配置成一个标准的Servlet,该控制器负责拦截所有HTTP请求,然后根据用户请求决定是否需要调用业务逻辑控制器,如果需要调用业务逻辑控制器,则将请求转发给Action处理,否则直接转向请求的JSP页面。
业务逻辑控制器负责处理用户请求,但业务逻辑控制器本身并不具有处理能力,而是调用Model来完成处理。
Struts 1提供了系统所需要的核心控制器,也为实现业务逻辑控制器提供了许多支持。因此,控制器部分就是Struts 1框架的核心。有时候,我们直接将MVC层称为控制器层。
提示:对于任何MVC框架而言,其实只实现了C(控制器)部分,但它负责用控制器调用业务逻辑组件,并负责控制器与视图技术(JSP、FreeMarker和Velocity等)的整合。
对于Struts 1框架而言,因为它与JSP/Servlet耦合非常紧密,导致了许多不可避免的缺陷,随着Web应用的逐渐扩大,这些缺陷逐渐变成制约Struts 1发展的重要因素,这也是Struts 2出现的原因。下面具体分析Struts 1中存在的种种缺陷。
(1)支持的表现层技术单一。
Struts 1只支持JSP作为表现层技术,不提供与其他表现层技术(如Velocity、FreeMarker等)的整合。这一点严重制约了Struts 1框架的使用,对于目前很多Java EE应用而言,并不一定使用JSP作为表现层技术。
虽然Struts 1处理完用户请求后,并没有直接转到特定的视图资源,而是返回一个ActionForward对象(可以将ActionForward理解为一个逻辑视图名),在struts-config.xml文件中定义了逻辑视图名和视图资源之间的对应关系,ActionServlet得到处理器返回的ActionForword对象后,可以根据逻辑视图名和视图资源之间的对应关系,将视图资源呈现给用户。
从上面的设计来看,不得不佩服Struts 1的设计者高度解耦的设计:控制器并没有直接执行转发请求,而仅仅返回一个逻辑视图名,实际的转发放在配置文件中进行管理。但因为Struts 1框架出现的年代太早了,那时候还没有FreeMarker、Velocity等技术,因而没有考虑与FreeMarker、Velocity等视图技术的整合。
提示:Struts 1已经通过配置文件管理逻辑视图名和实际视图之间的对应关系,只是没有做到让逻辑视图名可以支持更多的视图技术。
虽然Struts 1有非常优秀的设计,但由于历史原因,它没有提供与更多视图技术的整合,这严重限制了Struts 1的使用。
(2)与Servlet API严重耦合,难于测试。
Struts 1框架是在Model 2的基础上发展起来的,因此它完全是基于Servlet API的,所以在Struts 1的业务逻辑控制器内,充满了大量的Servlet API。
(3)代码严重依赖于Struts 1 API,属于侵入式设计。
Struts 1的Action类必须继承Struts 1的Action基类,实现处理方法时,又包含了大量Struts 1 API,如ActionMapping、ActionForm和ActionForward类。这种侵入式设计的最大弱点在于,一旦系统需要重构,这些Action类将完全没有利用价值。
可见,Struts 1的Action类这种侵入式设计导致了较低的代码复用。
2.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框架结构如图1-10所示。
图1-10 WebWork框架结构图
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中的拦截器由系统提供,因此无需用户参与。
开发者自己实现的业务逻辑控制器只是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。
(2)Action无需与WebWork耦合,代码重用率高。
Struts 1中的Action类需要继承Struts 1的Action类。我们知道,实现一个接口和继承一个类是完全不同的概念。实现一个接口对类的污染要小得多,该类也可以实现其他任意接口,还可以继承一个父类;但一旦已经继承一个父类,则意味着该类不能再继承其他父类。
得益于WebWork灵巧的设计,WebWork中的Action无需与任何Servlet API、WebWork API耦合,从而具有更好的代码重用率。
(3)支持更多的表现层技术,有更好的适应性。
从图1-10中可以看到,WebWork对多种表现层技术,如JSP、Velocity和FreeMarker等都有很好的支持,从而给开发更多的选择,提供了更好的适应性。
3.Struts 2
虽然Struts 2号称是一个全新的框架,但这仅仅是相对Struts 1而言的。Struts 2与Struts 1相比,确实有很多革命性的改进,但它并不是新发布的新框架,而是在另一个赫赫有名的框架——WebWork的基础上发展起来的。从某种程度上来讲,Struts 2没有继承Struts 1的血统,而是继承了WebWork的血统。或者说,WebWork衍生出了Struts 2,而不是Struts 1衍生出了Struts 2。因为Struts 2是WebWork的升级,而不是一个全新的框架,因此其稳定性、性能等各方面都有很好的保证,而且它吸收了Struts 1和WebWork两者的优势,因此,它是一个非常值得期待的框架。
Apache Struts 2是一个优雅的、可扩展的JAVA EE web框架。框架设计的目标贯穿整个开发周期,从开发到发布,包括维护的整个过程。Apache Struts 2就是之前的WebWork 2。在经历了几年的各自发展后,WebWork和Struts社区决定合二为一,也就是Struts 2。
经过五年多的发展,Struts 1已经成为一个高度成熟的框架,不管是其稳定性还是可靠性,都得到了广泛的证明。但由于它太“老”了,一些设计上的缺陷成为它的硬伤。面对大量新的MVC框架的蓬勃兴起,Struts 1也开始了血液的更新。
目前,Struts已经分化成两个框架:第一个框架就是传统Struts 1和WebWork结合后的Struts 2框架。Struts 2虽然是在Struts 1的基础上发展起来的,但实质上是以WebWork为核心的, Struts 2为传统Struts 1注入了WebWork的设计理念,统一了Struts 1和WebWork两个框架,允许Struts 1和WebWork开发者同时使用Struts 2框架。
Struts 2非常类似于WebWork框架,而不像Struts 1框架,因为Struts 2是以WebWork为核心,而不是以Struts 1为核心的。正因为这样,许多WebWork开发者会发现,从WebWork过渡到Struts 2是一件非常简单的事情。
当然,对于传统的Struts 1开发者,Struts 2也提供了很好的向后兼容性,Struts 2可与Struts 1有机整合,从而保证Struts 1开发者能平稳过渡到Struts 2。
Struts 2的体系与Struts 1体系的差别非常大,因为Struts 2使用了WebWork的设计核心,而不是使用Struts 1的设计核心。Struts 2大量使用拦截器来处理用户请求,从而允许用户的业务逻辑控制器与Servlet API分离。
Struts 2仍是以前端控制器框架为主体的。这意味着:
■ Actions仍然是通过URL触发的。
■ 数据仍然是通过URL请求参数和Form参数传送到服务端的。
■ 所有Servlet对象(如request、response和session等)仍在Action可用。
以下是请求处理过程的高层概览,如图1-11所示。
图1-11 Struts 2请求处理过程
整个请求的处理过程可以分为6步:
(1)由框架产生一个请求并进行处理。框架根据请求匹配相应的配置,得到使用哪些拦截器、Action类和返回结果的信息。
(2)请求通过一系列的拦截器。拦截器和拦截器组可以按照不同级别进行组合配置来处理请求。它们为请求提供各种预处理和切面处理的应用功能。这和Struts使用Jakarta Commons Chain构件的RequestProcessor类很相似。
(3)调用Action。产生一个新的Action对象实例,并提供请求所调用的处理逻辑的方法。我们在第二部中将对这一步骤进行进一步讨论。Struts 2可以在配置Action时为请求分配其指定的方法。
(4)调用相应的Result。通过匹配处理Action方法之后的返回值,获取相应的Result类,生成并调用它的实例。处理Result可能产生的结果之一就是对UI模板(但并非只有一个)进行渲染,来产生HTML。如果是这种情况的话,模板中的Struts 2 tags可以直接从Action中获取要被渲染的值。
(5)请求再次经过一系列拦截器处理后返回。Request以与进入时相反的方向通过拦截器组,当然,可以在这个过程中进行回收整理或额外的处理工作。
(6)响应被返回给用户。最后一步是将控制权交还给Servlet引擎。最常见的结果是把渲染后的HTML返回给用户,但返回的也可能是指定的HTTP头或进行HTTP重定向。
Struts 2和Struts 1的差别中,最明显的就是Struts 2是一个pull-MVC架构。这是什么意思呢?从开发者角度看,就是说需要显示给用户的数据可以直接从Action中获取,而不像Struts 1那样必须把相应的Bean存到Page、Request或Session中才能获取。
4.Struts 1和Struts 2的对比
下面从10个方面对Struts1和Struts 2进行比较。
(1)Action类的比较。
■ Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
■ Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts 2提供一个ActionSupport基类去实现常用的接口。Action接口不是必需的,任何有execute标志的POJO对象都可以用做Struts 2的Action对象。
(2)线程模式的比较。
■ Struts 1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts 1 Action能做的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
■ Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题(实际上Servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)。
(3)Servlet依赖。
■ Struts1 Action依赖于Servlet API,因为当一个Action被调用时,HttpServletRequest和HttpServletResponse被传递给execute方法。
■ Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts 2 Action仍然可以访问初始的request和response。但是,其他的元素减少或消除了直接访问HttpServetRequest和HttpServletResponse的必要性。
(4)可测性。
■ 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使测试要依赖于容器)。一个第三方扩展—Struts TestCase,提供了一套Struts 1的模拟对象来进行测试。
■ Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
(5)捕获输入。
■ Struts 1使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用做ActionForm,开发者经常创建多余的类捕获输入。动态Bean (DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的JavaBean)。
■ Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过Web页面上的taglibs访问。Struts 2也支持ActionForm模式。rich对象类型包括业务对象,能够用做输入/输出对象。这种ModelDriven特性简化了taglib对POJO输入对象的引用。
(6)表达式语言。
■ Struts 1整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
■ Struts 2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言—“Object Graph Notation Language”(OGNL)。
(7)绑定值到页面(View)。
■ Struts 1使用标准JSP机制把对象绑定到页面中来访问。
■ Struts 2使用“ValueStack”技术,使taglib能够访问值而不需要把你的页面(View)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(View)。
(8)类型转换。
■ Struts 1 ActionForm属性通常都是String类型。Struts 1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
■ Struts 2使用OGNL进行类型转换。提供基本和常用对象的转换器。
(9)校验。
■ Struts 1支持在ActionForm的validate方法中手动校验或通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
■ Struts 2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性。
(10)Action执行的控制。
■ Struts 1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
■ Struts 2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。