十二 06

2.1. Talking the talk
本章深入探讨Struts 框架,以及它能给你的应用开发所带来的诸多好处。我们相信,一旦你也能“随便谈谈”web 架构和设计,你就可以很好的在你的应用中使用Struts。

为了能对Struts 架构有个充分的全面印象,我们将总体介绍Struts 的控制流和它处理请求-响应事件循环的方式。只有彻底理解这个处理原理才能最好的在应用中使用这个框架。

选择一个web 应用框架不应该是个漫不经心的决定。很多人都可以使用这本书,特别是用这章的内容,来评价Struts 是否适合它们的项目。因此,我们在这章的最后部分将有一个关于Struts 优缺点的客观评价,并阐明其总体性能。

Struts 设计来针对专业开发人员。为做出正确的决策,专业人员应该知晓工具的能力和限制。

2.2. 为什么我们需要Struts

今天的web 应用基本上都是代表共同理念的关键组件。通常,开发团队需要在有限的时间里创建应用,然后它们不得不正确的构建,并能持续构建它。

Java web 开发人员已经有一些工具可用来建立表现层,比如JavaServer Pages 和 Velocity 模板。也有一些机制来处理数据库—如JDBC 和Enterprise JavaBean (EJB)。但我们用什么来将它们集合在一起?我们已经有了型材和砖墙…还缺什么呢?

2.2.1. 退一进三

在上世纪80 年代,当GUI 被发明时,软件架构认为,应用具有3 个主要部件:

管理数据的部件,创建屏幕和报表的部件,以及处理用户交互和子系统交互的部件[Ooram]。

在90 年代早期,ObjectWorks/Smalltalk 编程环境将这个三角结构引入为一个开发框架。按Smalltalk 80 的说法,数据系统称为模型Model,表现系统称为视图View,而交互系统称为控制器Controller 。许多现代开发环境,包括Java 的Swing, 都使用Model/View/Controller (MVC) 架构作为它们的基础架构。


图 2-1 MVC 架构

Java web 开发者已经有很多有用的工具,比如JDBC 和 JSP, 作为 Model 和 View 的手段,但作为控制器的工具和组件在哪?

2.2.2. 进入 Struts

Struts 的核心是一个MVC 风格的控制器。Struts 控制器搭起了Model 和View 之间的桥梁。框架也包括开发人员想用来开发可伸缩的、先进的应用系统的其他组件。Struts 是一个“ 隐藏支柱”的集合,帮助开发人员将分散的材料,如数据库和页面,结合成一个整体的应用系统。

2.2.3. Struts控制器组件

Struts 控制器组件是一个可编程的组件集,允许开发人员定义它们的应用如何准确地和用户进行交互。这些组件在逻辑名称后面隐藏了令人讨厌的、繁琐的实现细节。开发人员可以一次性编写这些实现细节,然后转头考虑它们的应用应该做什么,而不是考虑应用应该如何做。

用户通过超链接和HTML form 与Web 应用程序进行交互。超链接引导页面显示数据和其他内容,如文本和图像。表单通常通过一些定制动作向应用提交数据。


图 2-2 主要Struts 组件

如图5 中所示, Struts 提供了开发人员可用来定义超链接,表单,和定制动作这些交互的相关组件。我们已经使用这些组件在第1 章创建了一个入门程序。第3 章,我们还要用它们来创建另一个程序。然后,在第4 章,我们将讨论这些组件的详细配置。随后的章节,将详细讨论如何将每个组件以及如何用在你的程序之中。在第4 章,我们将展示如何在运行的程序上下文中使用这些组件。但是,因为这一章是架构性的总体介绍,所以我们继续介绍Struts 的主要部件。

 
超链接

对应用开发人员来说,超链接是指向应用中某些资源的路径。这些资源可能是web 页面,或者是定制动作。超链接中也可以包含特殊的参数。在Struts 中,开发人员可以将超链接定义为一个ActionForward 。

这些对象都有个逻辑名称和一个path 属性。这使得开发人员可以设置path,然后通过名称来引用ActionForward。ActionForward 通常在一个XML 文件中定义,这个配置文件在Struts 启动时载入。Struts 使用 XML 定义来创建Struts 配置,包括一个ActionForward 的列表。可用来创建到欢迎页面链接的ActionForward 对象的XML 元素看起来可能像:


这个元素事实上是创建了一个 ActionForward JavaBean ,其name 属性设置为welcome, path 属性设置为/pages/index.jsp。

JSP 页面和其它组件就可以引用这里定义的welcome 转发。Struts 框架将查找welcome ActionForward bean 并获取其path 属性以完成这个超链接。这样开发人员可以改变链接的目标而不用改变所有引用该链接的相关组件。在很多Web 应用中,象这样的细节被硬编码到JSP 或 Java code 中,使维护变得困难并且容易发生错误。在Struts 应用中,这些细节可以通过应用配置来改变,而不用触及到具体的页面和Java 类。

HTML表单

Web 协议,即HTTP 和 HTML,提供了一个从表单中提交数据的机制,但却把数据的接收作为一个难题留给了开发人员。为此,Struts 框架提供了ActionForm 类。ActionForm 设计来就是处理来自HTML 表单的输入:校验输入,重新显示表单以供用户进行修订(如果需要), 以及伴随着相应的提示和错误信息。ActionForm 其实是具有一些用来来管理校验和修订循环的标准方法的JavaBean。Struts 自动匹配JavaBean 属性和HTML 表单控件的属性。开发者只需定义ActionForm 类,余下的就交给Struts 。

例如,这个类将自动用HTML 表单中同名的属性来组装username 域:


其它属性也会根据表单中的每个输入域被自动加入。这使得其它组件可以从标准的

JavaBean 取得它们想要的属性。所以,完全不需要对HTTP 请求进行详细解析。

ActionForm 是按通常的JavaBean 类创建的。Struts 配置通过一系列描述符引用ActionForm 类: 和 元素。 元素是框架用来识别和实例化ActionForm 对象的描述符:


Struts 配置中要列出它使用的ActionForm bean 的清单,并给每个bean 一个在应用中被引用时的逻辑名(name 属性)。


定制动作

HTML 表单使用action 参数来告诉浏览器将数据送到何处。Struts 框架提供相应的Action 类来接收数据。框架会自动创建、组装、校验和最后处理Action 对象所对应的ActionForm。这样,Action 就可以直接从ActionForm bean 取得它需要的数据。比如下例:


Action 根据返回到控制器的ActionForward 对象作出控制流的决定。这允许Action 选择一个逻辑名称,比如continue 或者cancel,而不是具体的系统路径。

为保证可扩展性,控制器也传递当前的请求和响应对象。实际上, Action 可以做所有Java Servlet 可以做的事情。

 
除了ActionForward, ActionForm ,和 Action 对象,Struts 控制器层还提供了几个特殊的组件,包括ActionMapping 和ActionServlet 。 Struts 也提供在控制器层的应用程序的本地化。

ActionMapping
在一个web 应用中,每个资源都必须通过URI 来进行引用。资源包括HTML 页面,JSP 页面,和定制动作。为了给定制动作一个URI, 或者说路径,Struts 框架提供了一个ActionMapping 对象。象ActionForward 和 ActionForm 一样, ActionMapping 通常也在XML 配置文件中定义:


这也允许将同一个Action 对象定义为不同的ActionMapping。例如,其中一个映射要求校验而另一个映射不要求校验。

ActionServlet
Struts ActionServlet 完全在幕后工作,它将其他组件绑定在一起。虽然它也可以子类化,但大多数Struts 1.0 的开发人员将ActionServlet 处理为一个黑盒:他们只是配置它,然后让它自己工作。

在Struts 1.1 中,ActionServlet 是比较易于扩展的。第9 章将讨论Struts 1.1 ActionServlet 新的扩展点和配置选项。

本地化
Web 应用也通过各种提示和信息与用户进行交互。Struts 组件均有内建的本地化特征,以便Struts 应用可以为国际化用户使用。我们在此书中贯穿使用本地化特征。本地化的详细讨论见第13 章。

2.2.4. 用Struts开发Web应用

要使用Struts 开发web 应用,开发人员将需要的超链接定义为ActionForward,HTML 表单定义为ActionForm,定制的服务器端动作定义为Action 类。

需要访问JDBC 和EJB 的开发人员也可通过Action 对象进行他们的工作。这样,表现层不需要和Model 层打交道。Struts Action 对象将收集View 需要的数据,然后将它们转发到表现页面。Struts 提供 JSP 标记库,它们将和JSP 页面一起使用,简化 HTML 表单和访问Action 要转发的其它数据。其它表现机制,比如Velocity templates, 也可用来访问Struts 框架,以创建动态的web 页面。这种处理流程入下图:


图 2-3 数据回传给视图

关于和Struts 中如何使用各种数据系统,见第14 章。第10 章和第11 章学习如何创建 Struts 的表现页面。

在深入Struts 架构前,让我们看看一个Web 应用框架必须说明的问题。

2.3. 为什么需要框架

第1 章,我们介绍了应用框架,简短讨论了为什么框架很重要。但为了真正理解一个解决方案,我们需要了解问题所在。为web 开发应用虽然是值得的,但也要迎接各种挑战。让我们快速看看是什么使web 开发富有挑战。

2.3.1. Web—永无休止的修补

Web 开发者受到两种web 缺陷的影响。首先,我们希望使用浏览器作为客户端。其次,我们必须使用web 协议进行通讯。

Web 浏览器通过HTTP 协议通信,并用HTML 显示页面。Web 浏览器发送HTTP 请求,并渲染和显示它收到的响应。在处理很少改变的预先编好的页面时,这是个很好的平台。但我们大多都是编写动态程序,页面针对不同的用户是不同的。虽然有一些现成的动态特征的手段,web 仍然受到HTTP/HTML 的制约。

如下表所示: web 协议和客户端的限制,确定了如何编写Web 程序。

表格 2-1 HTTP/HTML 的限制

限制 导致的困难
缺省情况下,HTTP接收来自于网络上各种客户端的连接。但各种服
务器间的行为是不一样的;
首先,HTTP用简单的文本域来传输数据。传输二进制文件需要复杂
协议 的协议扩展
HTTP协议是无状态的,需要额外的努力来跟踪程序的用户
HTTP信赖并期望客户能提供正确的信息
客户端 浏览器是个独立的程序,处于应用的控制之外
所有的浏览器都是不一样的,都只支持官方标准的一个子集
从浏览器的输入可能是不正确或者不完整的。甚至可能是敌意的,  
和对程序有害的信息
HTML不能够建立一些在桌面环境中有的接口元素

用缺省数据建立HTML 控件对应用来说是个考验

很不幸,这种状况现在并没有些许改变。Web 开发人员在想战胜挑战时必须首先看到这些缺陷。因为这对编写强壮的Web 应用有太多障碍,使用框架便显得至关重要,免得你的应用陷入无休止的工作和改进之中。

在开发Web 应用时我们面临的挑战是很巨大的。但同时也是值得的。HTTP 协议和HTML 客户端使所有的人都可以访问你的应用。没有其他哪个平台能声称这样。

2.3.2. Servlet 解决方案
如第一章所述,Java Servlet 平台[Sun, JST] 扮演了一个基本框架,为Java web 应用提供了大量的能力。Servlet 提供了一个处理HTTP 请求和确保响应的基本接口。它在HTTP 之上构建了一个“会话” 上下文,帮助跟踪应用程序的用户。当然它也提供其他的上下文,帮助应用传输数据到浏览器或者应用中的其他servlet。Java web 应用也具有对基本安全特性的统一访问,而这些安全特性在不同的服务器上的管理是不一样的。

为了将这些内容集成在一起,Servlet 规范引入了一个容器来管理它们。容器也可以提供其他服务,比如作为一个JSP 的处理器。Servlet 容器可以包含它自己的web server,也可以简单的作为一个现有服务器的附属组件。

对数据库访问, Java 应用在其建议中有另外一个通用的框架: JDBC。开发者可以编写标准的SQL 接口,而将烦人的细节留给适配器来处理。这使得可以很容易的改变数据库厂商,而不用重写源代码。

为了取得远程服务器的高性能数据库访问, web 开发人员可以使用EJB 平台。大多数Java 应用框架,包括Struts, 都可以和EJB 一起使用。

总之,这使得基于Servlet 的web 应用非常轻便,并相对易于编写和维护。Servlet 和 JSP 在编写应用中扮演了完全不同的角色。象Strtus 这样的Java web 应用框架就构架于Servlet 之上,给开发者提供一个无缝的集成环境。

2.3.3. Servlet 框架
大多数,不是全部, Java web 框架使用 Sun Servlet 平台为基础。这些框架都绑定一些预制的servlet,你可以插入到你的应用中去。框架本身也包括一个类层次结构,这些类(或接口)你可以在你的应用中实现或者扩展。通常, 应用框架的目标是帮助你将你需要的数据从浏览器发出,进入到编程结构之中,这样你的应用就可以使用它—或者从编程结构中发出,进入到浏览器之中,这样你就可以看到。

一些框架,如Turbine [ASF, Turbine],也提供helper 类来使用 JDBC 数据库。其他框架,如Struts, 则是模型中立的。它们既不阻碍数据库访问,也没有提供帮助。而某些框架, 如dbForms [dbForms],则专注于数据库访问,而将其它任务留给开发人员或者其他框架。

通用框架策略
如下图所示, Java web 应用框架使用一些通用技术来帮助产品易于设计、编写和维护,这些技术包括:外部配置文件提供开发人员不想嵌入源代码中的实现细节。中心控制器提供一种方式,将HTTP 请求排入一个易于管理的队列。这种设计有时叫前端控制器模式(Front Controller [Go3])

外部表现系统

让不同的人同时工作在同一应用的不同部分。如, Java 工程师可以工作在和中心控制器相关的类,而页面设计者则专注于JSP。除了JSP, 其他表现系统,如Velocity Templates 或者 XLST, 都可以和Struts 一起使用。

框架通常有各种组件,但基本上都共享这个特性。这些公共策略早已在一些书象Design Patterns [Go4] 和Core J2EE Patterns [Go3]给出的范例中根深蒂固了。许多开发者在讨论并使用这些模式,但也许还没有第一次在Web 环境中实现它们。使用设计模式,如MVC, 使你可以容易的通过做正确的事情来构建你的应用。在桌面环境中使用设计模式的优点已经众所周知了,但在Web 环境部署这些模式却对大多数开发者来说还是不确定的。


图 2-4 通常使用配置文件,控制器和表现系统的框架

2.3.4. 黑盒-白盒统一体

框架有时分为两极标有白盒和黑盒的统一体[Fayad]。白盒框架严重依赖于面向对象的语言的特征,如继承和动态绑定。黑盒框架则注重定义可插入组件的接口,然后基于这些接口提供基本的起始组件。接口和基本组件通常提供热点(hotspot)方法,这个方法可以直接使用或者重写后提供特别的行为。


像许多正在使用的框架, Struts 使用混合的黑盒和白盒技术。但总体上,框架将偏向统一体的黑盒一端。

黑盒框架通常依赖于设计模式。Struts 也不例外。事实上,设计模式通常被用来作为框架的总体描述 [Johnson]。为保持这个趋势,我们先介绍一下设计模式,以及它们是如何用在Struts 框架之中。

2.4. Struts, Model 2,以及 MVC
Struts 关于自己要说的第一句话就是:框架…鼓励应用架构基于Model 2 方法,即经典的MVC 设计模式的变体这句话打消了一些web 开发者的疑虑,对那些还没深入Model 2 或 MVC 的人来说却更

加疑惑了。事实上,没有深入了解MVC 和Sun Model 2, 要理解很多Struts 的文章都很困难。

2.4.1. MVC的演化
如2.1 节所说, Model/View/Controller 原本是建立Smalltalk 应用的框架。框架支持代表应用状态、屏幕表现和控制流的3 个类,分别叫做 Model, View, 和 Controller 。


图 2-5 MVC 三角

Smalltalk MVC 框架在颇为流行的书Design Patterns: Elements of Reusable Object-Oriented Software[Go4] 是作为案例来研究的。Design Patterns 这本书有四个作者,被称为“四人帮GoF ”。

Design Patterns 中的MVC 例子称为通知/订阅者(notify/subscribe)协议和观察者(Observer) 模式的使用。例子的基础是,对同一数据,系统可能需要不同的显示视图,比如条形图、饼图、数据表格等等。这是一个划分应用的精彩理由,经常被重复引用。


图 2-6 模型数据可以被用于不同的视图中

在图2-6 所示的例子中,每种视图可能在同一时间显示给不同的用户。应用必须保证在其下面的数据或者模型改变时视图的更新。为改变模型,用户提交一个请求给控制器,由控制起来配合改变模型。数据视图必须跟着改变,以反映最近的模型改变状态。

Smalltalk MVC 方案使用观察者通知模式。在这种模式下,每个视图注册为一个模型数据的观察者。然后模型可以通过发送消息给所有这册观察者,通知它们相关的改变。其为Smalltalk MVC 框架已经通用化了,他也可以将它应用到其他平台上面。

2.4.2. Model 2 的出现

JSP 的意图是使创建动态页面更容易。JSP 首先是作为servlet 的替代引入的,还有就是MS 的ASP。Servlet 的强大功能当作易于创建服务器页面的工具提供给开发者。但强大的功能伴随着巨大的责任。很多团队发现,如果他们一不小心,他们的项目就会因为纠缠如麻的页面变的容易崩溃。进一步的特性需要使用复杂的脚本程序。但脚本程序是非常难于重用的— 除非你在页面间把代码“拷贝粘贴”。

工具页面也可以包括进来,但它们很难被组织在一起,并且造成非常丑陋的“资源”树。有些东西会出错。

很多开发人员很快意识到, JSP 和servlet 可以一起使用来部署web 应用。Servlet 可以应付控制流,而 JSP 则可专注于讨厌的编写HTML 的任务。在这种情况下,结合使用 JSP 和 servlet 开始被称为Model 2 ( 单独使用JSP 称为Model 1) 。

当然,从Sun 那里仍然没什么新东西… 而且很多人很快指出 JSP Model 2 类似于经典的Model-View-Controller 架构。

在很多场合,现在交互使用Model 2 和 MVC 这两个词已经很平常了,虽然还有一些争论,即一个应用是否是 MVC,以及是否支持经典的观察者通知模式。没有观察者通知的Model-View-Controller 有时被称为MVC2 或Web MVC 。.

2.4.3. 应用层—视图之间的去耦合

Model 2 被认为区别于MVC 的一个原因是,观察者/通知模式不能在web 环境内工作的很好。HTTP 是一个 “拉” 的协议: 客户请求然后服务器响应。没有请求就没有响应。观察者模式需要一种“推”协议来进行通知,以便服务器能在模型改变时将信息推送到客户端。虽然也有一些方法能模拟将数据推送到客户端,但这和基本情况相悖,并且会视为是个权宜之计的修补。


图 2-7 MVC 通常表示为3 个互相连接的组件

图2-7 是典型的Model-View-Controller 范式,经常被表示为:一个互相连接的三角形。在web 应用中维护范式中的“通知改变”部分是非常困难的。

这些东西在所有资源都在一台服务器上,而且客户端保持一个开放连接的情况下工作得非常好。如果资源分布在不同的服务器上,并且客户端不能维护一个开放的连接情况下,工作的并不理想。

许多分布式系统架构,包括web 应用,在视图进行状态查询的概念时退缩了。绝大多数情况下,远程应用是按层模式[POSA]设计的。基本上,层模式下,层内的对象可以和同一层或者相邻层的对象进行通信。在一个复杂应用中,这可以在添加组件时,防止依赖关系呈指数增长。在设计远程应用时,分层是一个核心模式。

从MVC 上下文中,引入层模式将状态改变和状态查询的职责加于控制器之上,并伴随着改变通知。


图 2-8 Web 应用的层模式

如图2-8 , 分层的web 应用使用一种比传统MVC 模式更加“扁平”的模式。控制器被夹在表现层(View) 和应用逻辑 (Model)之间。

每个组件的主要职责并没有改变。流程有轻微改变,即查询状态和改变通知都必须通过控制器。另一个改变是,当视图,或者表现层需要渲染动态页面时,它使用从控制器传递的数据而不是直接来自于模型层。这种改变去除了View 和 Model 的耦合,允许控制器选择数据和显示这些数据的视图。

2.4.4. Struts如何实现Model 2, MVC, 和层

Struts 通过提供一个控制器Servlet 实现了Sun 的 Model 2 架构,这个控制器可以用来管理JSP 页面和其他表现设备之间的流程。Struts 通过使用ActionForward 和ActionMapping 来保证表现层之外的控制流决策来实现 MVC/层模式。JSP 可以引用一个逻辑目标。控制器组件在运行是提供准确的URI。

表列出了Struts 的核心类,即对应的经典的MVC 组件职责。

表格 2-2 核心 Struts 类和MVC 的对应

类 描述
ActionForward  用户指向或者视图选择
ActionForm  状态改变的数据
ActionMapping  状态改变事件
ActionServlet  控制器,接受用户请求和状态改变,以及发出视图选择
Action  控制器的一部分,与模型交互,执行状态改变或状态查询, 以及告诉ActionServlet 下一个选择的视图

除了这些核心类, Struts 使用一些配置文件和视图助手(view helpers )来沟通控制器和模型。下表列出了Struts 配置文件和描述了他们在架构中的角色。

表格 2-3 Strtuts 配置文件

文件 目的
ApplicationResources.properties  存储本地化信息和标签,以使应用可以国际化
struts-config.xml  存储控制器对象的缺省配置,包括模型支持的用户指向,
状态改变,状态查询

为将Struts 配置数据暴露给视图,框架以JSP 标签的形式提供了大量的助手类,如表:

表格 2-4 Strtus 视图助手

标记库描述符 目的
struts-html.tld  扩展HTML Form 的JSP标记
struts-bean.tld  扩展处理JavaBean的JSP标记
struts-logic.tld  扩展测试属性值的JSP标记

将以上内容放在一起,下表按层列出了Struts 组件:

表格 2-5 Struts 组件,按层索引

视图层 控制器层 模型层
ActionForward  
ActionForm 类  
JSP 标签扩展 ActionMapping ActionServlet  GenericDataSource  
Action 类  
ActionErrors  
MessageResources  
JSP, Velocity 模板, 以及其他表现系统 各种工具类, 比如CommonsDigester 和CommonsBeanUtil  开发者提供的其他数据服务和API  

实践中,控制器与视图的交互通过请求,会话以及Servlet 平台提供的应用上下文进行。

(2.3.2)。
控制器和模型的交互通过文件和数据存储系统完成 (比如装入XML 文档或者属性文件),或者通过其他服务,如TCP, 创建一个到JDBC 数据库的连接。

2.5. Struts 控制流
因为web 应用是动态的,所以很难表现“一个真正固定的控制流”。取决于环境,不同的方式下有很多不同的事情发生—特别是在web 应用中。但是事情仍然有一个通用的秩序。

如果你是个Struts,应用框架,甚至web 应用的新手,这些流程刚开始可能难以跟得上(理解)。亟待解决的各种问题不一定那么明显。我们将在本书中慢慢详细涉及。首先,在介绍树木之前我们先认识这片森林。你读完此书后,我们建议你再次回来,看看每一部分是如何切合进这个总图的。

2.5.1. 总图


图 2-9 Struts 请求-相应流程

图2-9 以UML以次序图的方式展示了Struts 请求-响应流程。我们来按这个请求-响应流程走一遍。括号内的数字请参照图11中的相关地方:„ 客户请求匹配Action URI 样式的路径 (1). „ 容器将请求传递给ActionServlet. „ 如果这个是模块化应用,ActionServlet 选择响应的模块。„ ActionServlet 查询路径的映射。(来自于配置文件)„ 如果映射标明了一个form bean,ActionServlet 看是否已经有一个实例,或者创建一个新的实例 (1.1)。如果已经有一个form bean,ActionServlet 重设它,并根据

HTTP 请求重新组装它。
„  如果 mapping  的validate 属性设置为 true,  它将调用 form bean  的
validate 方法(1.2)。    
„  如果失败,Servlet 将控制转发到input 属性标明的路径,控制流终止。

„ 如果mapping 标明一个Action 类型,如果它已经存在或已经实例化,它将被重用(1.3)。

„ Action 的perform 或execute 方法被调用,并传递一个实例化的form bean (或者 null)。

„ Action 组装form bean, 调用业务对象,以及其他需要做的事情。 (1.3.1-1.3.4)。

„ Action 返回一个ActionForward 给ActionServlet (1.3.5).

„ 如果ActionForward 指向另一个 Action URI,重新开始;否则,显示页面或者其他资源,流程结束。通常,结果是一个JSP 页面,或者Jasper, 或其它类似技术 (非Struts)渲染的页面。 (2, 3).

„ 如果JSP 中使用了Struts HTML 标记, 并且在请求中看到正确的ActionForm

(1.1), 他们会从ActionForm 中组装HTML 控件。否则, 标记将创建一个。从Struts 1.1 开始, 如果form 标记自行创建一个ActionForm ,它将调用ActionForm 的Reset 方法。如果你只是想创建一个空白的表单 (1.1), 你可以使用标准的ForwardAction(见第 8 章) 来通过Action 传递控制,然后离开页面。

2.5.2. 出色的细节

他们说,恶魔总是藏在最隐秘的地方。

前面章节的大纲和图示很好的显示了Struts 的概揽,但忽略了很多重要的细节。让我们深入到更好的地方。因为这里是HTTP, 所有东西都是从请求开始。

请求由容器接收

Struts 框架的核心组件是 ActionServlet。象所有的servlets, 它生存在容器中, 比如Tomcat, Resin, 或者 WebLogic 等。当容器启动时,读入部署描述符 (web.xml),告诉容器要装入哪些个servlet。

一个标准的servlet 设定是servlet mapping。容器使用这个设定来决定哪个请求将被送到哪个servlet:


这里,我们让容器将ActionServlet 映射到那些符合/do/* 样式的请求。这些请求可以是: /do/This /do/That /do/something/Whatever.

许多应用喜欢使用前缀:


URL 样式也可以使用象this.do 或 that.do 或 /something/whatever.do 的样式。可以用任何有效的扩展名,但.do 是比较简单和常用的选择。

当一个请求随着符合Servlet 上下文的路径组件提交时,容器将其转发给ActionServlet 。不匹配的请求则不转发到ActionServlet。比如,匹配*.jsp 的请求将直接转发给容器的 JSP 服务,比如Jasper(如果你使用Tomcat 或者WebSphere 的话)。在应用中可以有其他的servlet 来处理其他格式的请求路径。不匹配所有servlet mapping 的请求将直接送给容器默认的web server。

请求被ActionServlet接收

当ActionServlet 收到一个请求, 它通过一系列的流程处理locale, mapping, form bean, 最后是Action 来运行这个请求。这里某些步骤只在Struts 1.1 应用才有:

„  处理多部分请求.  如果请求是个多部分(multipart)请求 (比如,一个表单加上一个MIME  
附件), Servlet 用一个特殊的句柄包装这个请求,避免处理错误。
„  处理路径ActionServlet 首先检查这个请求路径是否是一个应用模块。如果是,相应模
块的配置被选中。[Struts 1.1]  
„  处理场所.(local) 默认下,ActionServlet 会检查是否有一个标准的locale 对象在用户会
话的上下文当中。如果没有, ActionServlet 将放入一个。这个对象将为每个用户提供
本地化表现。

„ 处理内容和缓存默认的MIME 类型和可选的请求头将加在响应之上。

„ 处理映射( Mapping) ActionServlet 检查ActionMapping ,是否有注册的路径符合正处理的请求。如果没找到,ActionServlet 转发到默认(或 “unknown”) Action (如果设定有默认或未知Action),否则,产生一个 “bad request” 错。如果找到相关映射,将被放入

请求之中,供后面引用。

„ 处理角色. ActionServlet 检查是否用户被授权可以访问action. [Struts 1.1]。

„ 处理ActionForm. ActionServlet 检查是否mapping 中指定了一个ActionForm. 。如果是,

servlet 检查是否已经有一个存在于特定的范围之内(默认是会话)。如果不存在,ActionServlet 创建一个。

„ 处理组装. ActionForm 的reset 方法被调用,然后通过反射机制被自动组装。匹配ActionForm 属性的参数将被使用。其他参数和属性被忽略。

„ 处理验证ActionForm 的validate 方法被调用。如果方法返回false, 控制传递给ActionMapping 的input 属性标明的路径, Action 未被处理。

„ 处理转发和包含如果ActionMapping 标明forward 或include 属性,控制被传递给其他资源。否则ActionServlet 将请求委托给一个Action 对象。

„ 处理 Action. 如果mapping 标明一个Action 类型, ActionServlet 检查是否已经有一个被实例化了。如果没发现, Action 对象将被实例化。每个类只能有一个Action 对象(Singleton 模式),它通过多线程处理所有对它的请求。Servlet调用 Action 的perform 或execute 方法,传递请求, 响应, mapping, 以及form bean 。

Action 执行要求的行为,可以包括:

„ 访问数据系统,比如JDBC 数据库

„ 在请求中创建被视图使用的对象

„ 如果需要,在会话中创建对象

„ 根据需要更新会话对象,比如用户的场所

„ 执行需要的业务功能

„ 处理以外和其他错误条件

„ 发送一个直接发送一个响应,或者返回一个ActionForward 给servlet

这里某些行为,象访问数据库,通常由Action 调用的业务对象来处理( Business Delegate

模式)。 Action 处理一些web 特定的任务,可以放在业务对象中的代码都应该放入业务对

象。Action 是一个控制器类,不应该用来处理业务的核心逻辑。

Action返回ActionForward
Action 完成后,它返回一个ActionForward 。如果 ActionForward 为null, ActionServlet 假定响应产生了,但不做任何事情。否则, ActionServlet 读入 ActionForward , 重定向或者转发请求到相应的资源。

如果请求是另一个Action URI, 容器将请求返回给ActionServlet 。否则容器发送请求到其它servlet 或service。

如果ActionForward 设为重定向(redirect), 请求被发送回客户端,并提示重新提交一个新请求到特定的地址。

由Jasper (或类似的东西) 渲染JSP页面

ActionServlet 发送一个请求到 JSP, 请求是被另外的服务处理, 如 Jasper 。典型地, Struts 和其他标签扩展用来编写页面的动态部分。有时,也使用JSP 模板,以便页面可以从其它组件进行构建。

通常, 动态数据在JavaBean 中传递到请求上下文中的页面。这就是熟知的视图助手(View Helper )模式 [Go3]。标签扩展简单的调用JavaBean 的方法,并返回格式化的数据。而数据如何被放入页面中那是表现逻辑的事情。数据本身的格式通常是业务逻辑的一部分,所以委托给了bean。

Struts 标记也可以访问框架提供的视图助手。这些包括本地化标签和提示,错误信息,以及超链接路径。另外,Struts 标记可以计算表达式,通过列表反复,以及在HTML 表单中组装控件。

其他 servlet 渲染响应

处理完Action 后,请求可以被送到应用中的其他 servlet 或服务。其他表现系统,如 Velocity templates, 可以通过servlet 上下文访问框架资源。

2.5.3. Struts 是富有效率的吗?

详细描述完Struts 处理流程后,你可能会想知道这些的花多长时间。通常,Struts 应该能提升大部分正确设计的Web 应用的性能。在本节中,我们检查一些关系到框架效率的特殊设计点。


Struts 不仅是线程安全(thread-safe)而且是线程依赖(thread-dependent)的

Struts 使用轻量的Action 对象,而不是各个单独的servlet,来对请求处理响应。 Struts 实例化每个Action 类一次,并允许其他请求通过原有的对象线程化。这种核心策略节省了资源,并提供最大的吞吐性。一个正确设计的应用将通过使用一个单独的Action 来路由各种相关操作来发挥这种特征。

ActionForm bean 最小化子类代码并缩短子类层次

Struts 框架的一个关键点是可以从请求中自动组装ActionForm bean 。没有这个组件, 用户不得不自行编写代码并实例化来组装每个bean 类。小心使用反射机制会节省不少资源,资源是有限的,并允许它们更好的使用。

Struts 标签库提供通用功能

Struts 一起提供的bean 和 logic 标记库符合大部分JSP 标记的需要。它们减少甚至消除了编写额外标签的需要。JSP 规范就包含了JSP 中的标签重用。使用相同的通用标签3 次比使用3 次不同的标签来的有效率。

Struts 组件对应用来说都是可重用的

框架绑定的工具可以在大部分应用中使用。BeanUtil.populate 方法就是个例子。这个方法用来从HTTP 请求组装一个ActionForm Bean,但也可以用来从其它类型的映射中组装一个FormBean。重用组件可以减少开销和节省资源。

Struts 本地化策略减少了大量冗余JSP

通过允许本地化页面在运行时才获取,国际化应用可以为每种可能需要的语言只提供一个单独的页面。同时,相同的消息系统也可以用于处理错误信息。同一对象提供了双重用途。

Struts 设计为一个开放架构

Struts 组件设计来是可以被应用子类化的,以便可以提供其它的服务功能。这使得开发人员可以扩展存在的类而不是重新编写新类。而且,Struts 也和应用共享资源。这时开发人员可以使用存在的组件,而不用编写和实例化它们自己的类。

Struts 是轻量型架构

类似的框架也许提供数百个类和几十个包。整个Struts 框架由5 个标记库和5 个核心包组成。

Struts 是标准兼容的

Strtus 在许多运行标准组件的容器上都工作的非常之好。

Struts 是开源的,具良好的文档

这意味着开发人员可以检查源代码,找出一些潜在的瓶颈。而且Struts 是模型中立的。因为Struts 并没有对后端模型做任何假定,一个应用可以按其最有效率的方式实现模型层。Struts Actions 可以调用一系列助手类来访问需要的数据。一旦数据被检索到,对JavaBean 的依赖,使Struts 更容易保持值对象,这样来减少了大量的模型层调用。

2.6. Struts的长处和弱点

Logging(记录) Struts 使用容器的缺省记录系统,没有提供一个自己的记录包来为应用提供记录机制。(Struts 1.1 实现了 Commons-Logging 接口包)  
每个应用装入一个单独的配置文件 大型项目可能需要使用多个不被整个团队共享的配置文件(Struts 1.1 支持多配置文件)  
每个场所装入一个单独的资源文件 大型项目可能需要使用多个不在整个团队共享的资源文件。(Struts 1.1 支持多资源文件)  
没有服务管理器 ActionServlet 必须被子类化来提供附加服务,比如用户记录器或者身份认证系统(Struts 1.1 提供一些新的扩展点和组件)  
ActionForm 官样文章 Struts 1.0 希望开发人员创建定制JavaBeans 与HTML 输入表单一起使用(Struts 1.1 支持 Map 和 DynaBean 来代替定制JavaBean.)  

表2-6 列出了框架当前的弱点。在下一节,我们将详细的讨论它们,并象资产一样描述。“Goofus” 或者 “Gallant,” 选择在你。

表格 2-7 Struts1.1 的弱点

弱点 说明
没有事件模型 Struts 紧密和HTTP的请求-响应模型结合,这限制了开发人员更
好地处理事件
调试 不支持自动调试(除错),开发人员不得不手工创建“断点”,
并向容器的记录系统写标准输出
没有缺省的数据模型或 访问持久数据模型留给了开发人员
者具体的推荐  
单一ActionServlet  一个应用终止可以使用一个单一的ActionServlet, 这个限制可能
导致配置冲突
需要理解Struts 组件 开发人员需要理解一些特殊的类以及他们如何交互。
不能提供优先技术支持 ASF是个志愿者组织,没有全职人员提供可担保的响应
Mailing list已经成为知 Struts 有一个日益增长的邮件列表,但要在其中找到最好的间以
识的障碍 非常困难。
正式发布版本并不快速 Struts正式发布版相对于其他项目来说先得慢了。开发人员必须
经常检查“每日构件”或的最新改进。
i18n 限制 Struts 的消息资源对建立国际化的资源和错误信息非擦好,但不
适合于处理大文本块
JSP mindset  因为使用MVC架构, 使得资源对所有表现层都是有效的。这是个
长期的弊病对JSP来说。
JSP意外本地化 很多系统级消息,象JSP意外,都没有本地化,通常显示为英语
标记属性冗长 许多标记扩展要求很多参数,对编程来说很笨
perform 和 execute 方法体 Struts 架构的关键是将请求委托给一个 Action类或者叫分发者dispatcher。Action 类是Struts支持的唯一分发者,并仅通过其perform 方法来调用。这将应用限制在只能和perform 方法传递的数据一起工作。即使有办法超出这个限制, perform 方法也
是个瓶颈。
一个通常的请求要求ActionServlet组装几个ActionForm。但是因
为 perform 接受单个ActionForm 参数,如果不经过较大的框架革新是不可行的。
Struts 1.1 添加了一个execute 方法,它有助于改善perform的其他主要缺陷:因为他返回意外。然而,主要的问题已就存在。
模糊的术语 Struts 框架在明显的增长。而给一些应用选项和
类的名称却容易让人混淆。例如,web.xml “ 中的validate” 选
项却和Action 对象的validate 方法无关,而和如何解析配置文
件相关。同样,神秘的 “ null”  
选项则表示当消息关键字未找到时,是否返回一个错误信息。
有个趋势是在类层次树种使用复合名称。在Action包中的每个类
都有个前缀为 “ Action,” 这却是多余和容易混淆的。同时,在Struts配置文件中, ActionMappings 定义的元素名是“ Action” 而不是“ ActionMapping” 。如果开发人员引用一个“ action,” 很难区别它们是指Action 类或是配置类的ActionMapping。
在 Struts 配置中, “ name” 域标识ActionForward 和
ActionForms“ path” 域标识 ActionMapping。“action-mapping 元素的 name” 属性则指出使用哪个ActionForm 。 ActionForward 的URI域也称为 “ path” ,但可以包括伴随path 的查询组件。到ActionMapping 的 “ path” 不包括servlet 样式, 象 *.do, 但是ActionForward 的path 却包括 *.do 扩展名。
应用资源其实是真正的消息资源。
等等。
凡此种种,这些小矛盾可以把一些新手搞糊涂,并且使框架难以
学习

2.6.2. Struts的强项

强项 说明
以HTTP为中心 Struts设计围绕标准 HTTP 请求-响应模式,为许多Web开发
人员所熟悉
标准记录 Struts 可以使用容器的缺省记录系统,而不需要配置和理解
其他包
可选的调试记录 Struts 可选记录大量状态处理时的信息,它们可以有助于进
行调试
模型中立 Struts 并不倾向于哪个特定的持久层
在一个中心配置中汇集实现 Struts 配置封装了应用,或者应用模块[Struts 1.1]的实现细
细节 节。所以它们可以作为一个整体评价和管理
允许为每个场所配置不同的 不同的语言翻译可以工作在他们自己的消息资源文件版本
消息资源 上。添加一个新场所的支持仅需简单地添加一个资源文件
轻量 Struts 仅有几个核心类要学习
开源 全部源代码在自由的 Apache 软件许可下,所有的选择都在

强大的开发人员团体 有一个强大的开发人员团体使用Struts 。邮件列表时非常活跃
的。许多开发人员的扩展是很成功的
强大的供应商团体 Struts 已经和其他一些产品合在一起,包括Jcorporate的s  
Expresso 和 IBM的 WebSphere。一些厂商也提供Struts专
用工具。
强大的产品支持 Struts 有其自身的专业管理的JGuru 论坛。Struts 邮件列表至少可以通过两种支持门户进行访问。
Struts 已经被许多文章和书籍涉及,并有一些组织提供专业的教程
强大的开发团队 超过30个开发人员为Struts 1.1 做贡献。Struts团队现在有九
个活跃的志愿者,他们全为源代码负责
稳定发布版本 Struts正式发布版本要经过长期的测试,并没有最后期限,所
以团队可以提供高质量的产品
i18n 支持 支持内建的国际化
高度兼容 Struts专注于提供公共标准100% 兼容的产品
全方位的标记扩展 Struts包括一系列通用标记扩展。他们一起可以符合你所有的
JSP 需要,而不用编写脚本程序


作者:Jock

Leave a Reply

You must be logged in to post a comment.

Switch to our mobile site