1.3 Struts2框架13 首先我们需要考虑的是,图1-4中的工作流程仍然遵循我们之前看过的简单MVC框架视图。 1 在图l-4中,Fi1 terDispatcheri已经完成了自身的控制工作,并选择了相应的动作来处理请求。 图14展示了动作被控制器调用之后实际发生的状况。如你所看到的那样,一些额外的部分被加 入到了MVC基础之中。我们会在下一段介绍在处理请求的过程中拦截器和ActionContext如何辅 助动作和结果。 图l-4引入了以下几个新的Struts2组件:ActionContext、拦截器、ValueStack和OGNL. 图1-4详细展示了Suts2内部实际发生的状况。可以说本书中讨论的所有内容都在图1-4中。由于 在请求处理周期中 一个遇到的是拦截器,所以我们从拦截器开始讲解。名字似乎很明确,但是 准确地说它们拦截什么呢? 1.拦截器 在研究图1.4的时候可能你已经注意到,在动作前有一细烂截器。对动作的调用以须经时这 组拦截器。这是Struts2框架的一个核心部分。在本书的后续内容中,我们会用整整一章来讲解这 个重要的组件。但是现在,只需要理解每一个动作都有一组拦截器与之相关联。拦截器在动作执 行之前、之后都被调用,并且需要指出的是它们在结果执行完毕后才被触发。拦截器没有必要在 两次触发时都做工作,但是它们确实有机会。有些拦截器仅在动作执行之前工作,而另外的一些 仅在动作执行之后才工作。最重要的是,拦截器允许常见的、横切的任务在简洁、可重用的组件 中定义,从而能够和动作的代码分离。 定义拦截器是一种可以在请求处理之前或者之后执行的Stus2组件。它提供了一种在其中定 义不同的工作流和横切任务的架构组件,以便能够简单地重用这些组件,并且能够与其 他架构关注点分离。 什么类型的任务应该在拦截器中完成?日志是一个很好的例子。日志应该在调用每个动作的 同时被记录,但是通常情况下不应该被放在动作内部。为什么呢?因为它通常不是动作工作单元 的一部分。如果细分,它应该属于高层系统管理的范畴。之前,我们指出框架有责任提供一系列 内建的功能解决方案来处理常见任务,例如数据验证、类型转换以及文件上传等。Struts2使用拦 截器完成这些工作。虽然这些任务很重要,但是它们与请求的动作逻辑没有什么特别的联系。 Struts2使用拦截器,同时分离和重用这些横切关注点。拦截器在Struts2框架中扮演非常重要的角 色。虽然你可能不会花费大量的时间在编写拦截器上,但是大部分开发人员会发现,使用自定义 的拦截器可以完美地解决很多任务。就像我们说过的那样,我们会用第4章整章来研究这个核心 组件 2.Va1ue8tack和OGNL 虽然拦截器不会消耗你太多的日常开发精力,但是你必须时刻牢记ValueStack和OGNL。 简要地说,ValueStack是保管与请求处理相关的所有数据的一个存储区域。你可以把它当成 个便签,在解决请求处理的问题时,框架会在这里工作。Suts2将所有相关的数据集中保存在 个方便的地方,而不是将数据传米传去,这个地方就是ValueStack
14第1章Struts2:现代Web框架 OGNL是一种访问存储在中央存储库(repository)中数据的工具。更准确地说,它是一个允 许你引用或者操作ValueStack中的数据的表达式语言。刚涉足Struts2领域的开发人员往往对 ValueStack和OGNL会有更多的问题。如果你来自于Struts 1,你会发现新框架有两个更奇特的 功能。出于这个原因,以及它们绝对的重要性,在本书中我们会认真地对待它们。第5章和第6 章将详细描述这两个框架组件的功能。 定义Struts2使用ValueStack作为请求处理过程中所需的应用程序域数据的存储区战。数据被 放入ValueStack为请求处理作准备,在动作执行过程中,数据在这里被操作。当结果呈 现到响应页面时,数据从这里被读取, ValueStack和OGNL微妙和强大的地方是它们不属于框架中任何独立的组件。回头看看图 1-4可以注意到,拦截器和结果都可以使用OGNL表达式语言来指向ValueStack中的值。 ValueStack中的数据跟着处理请求经过所有阶段,它贯穿框架的整个过程。之所以能做到这点 是因为ValueStack存储在一个线程本地对象(ThreadLocal)中,它的名字叫ActionContext. 定义OGNL是一个用来引用、操作ValueStack中数据的强大的表达式语言(还不止于此),. ActionContext包含所有的数据,这些数据构成了动作执行的环境。它包含ValueStack, 也包含框架会使用的内部数据,例如请求对象、会话对象以及从Servlet API中得来的应用程序映 射。如果喜欢,你自已可以访问这些对象,后续内容中会讲解如何访问。现在我们只关注 ActionContext作为ValueStack的ThreadLocal的存储场所。使用ThreadLocal可以在相同线 程执行中的任何地方访问ActionContext或者ValueStack。因为Struts2每一个请求的处理都发 生在一个单独的线程中,所以可以在框架处理请求的任何一个地方访问ValueStack. 通常情况下,直接获得ActionContext中的内容是一个不好的方式。框架提供了很多优雅的 方法在不接触ActionContext或者ValueStack的情况下与这些数据交互。我们主要使用OGNL 在框架的多个地方都使用OGNL来引用、操作ValueStack中的数据。例如,使要用OGNL将 HTML表单字段与ValueStack中的数据对象绑定起来以传输数据,并且使用OGNL将数据入到 JSP页而或者其他类型的结果的呈现过程中。现在你只需要理解,Va1ueSa©k是存储你正在使用 的数据的地方,OGNL是一种表达式语言,你和框架用它指向来源于请求处理周期不同部分的 数据。 现在,你已经了解了Struts2如何实现MVC模式,并且已经简要地了解了在处理真实请求过 程中所有其他重要的角色。在开始了解框架核心组件的详细内容之前,下一步需要做的就是通过 程疤备济兼然海前们冒藏消h道流位湾强,养出才行时运 ;前1.4.-小结mm 内容,现在你应该已经很好地理解了Struts2 本章开头讲解,了很多关于框架要和设计模式的抽象
1.4小结15 的架构。如果抽象内容不太适合你的胃口,那么知道本书的理论部分已全部讲完了你会很高兴。 赶快开始第2章吧,本书只涉及构建Wb应用程序所需的具体的、实践性的内容。但是在继续之 1 前,让我们花点时间回顾一下学过的内容。 也许我们应该花点时间来评估Suts2框架。基于对技术背景和常见领域任务的理解,在本章 的开始,我们给出了wb应用程序框架的两个职责。第一个职责是为Wb应用程序提供一个基础 的架构。我们已经看到Struts2这样做了,并且讨论了影响Suts2架构选择的设计模式基础。特别 是,我们也了解到Struts2利用从第一代web框架中学到的经验实现了一个全新的、更整洁的、基 于MVC的框架。我们也了解了实现MVC模式的具体的框架组件:动作组件、结果组件和 FilterDispatcher. Wb应用程序框架的另一个职责是自动化Wb应用程序领域的许多常见任务。这些任务有些 时候被称为横切关注点(cross-cutting concem),因为它们在应用程序动作的执行中一次又-一次地 出现。日志、数据验证以及其他常见的横切关注点应该从动作和结果的关注点中分离出来。在 Suts2中,拦截器提供了一种架构方面的机制,将这样的横切关注点从核心的MVC组件中分离 出来。随着深入学习本书,你会发现框架带有很多内建的拦截器,能够处理领域内所有的常见任 务。你会看到,它们不但能够处理大量核心框架功能,也可以处理只与你的应用程序相关的需求。 虽然你可以不用编写任何拦截器,但是我们还是希望关于拦截器的章节能够激起你编写自定义挡 截器的欲望。 我们也提供了一个框架实际处理请求的宏观介绍。我们看到每一个动作都带有一个在动作执 行之前或者结果执行之后被调用的拦截器栈.除了MVC组件和拦截器之外,ValueStacki和OGNL 表达式语言在框架内部存储、操作数据方面扮演了至关重要的角色。到现在为止,你应该大体了 解了框架到底可以做什么。在下一章的HelloWorld)应用程序中,你会看到一个框架组件的具体示 例。在这个示例之后,我们会继续研究框架的核心组件,从第3章对动作的研究开始
第2章 初识Struts2 本章内容 口声明应用程序的架构 口部署HelloWorld应用程序 口构建基于XML的应用程序 口使用Strutsi注解 在第1章中,我们已经了解了Wb应用程序领域,知道了设计模式和框架如何帮助开发人员 完成他们的工作,并且简单地了解了Sus2的架构和处理请求的管道。到目前为止,我们完成了 本书的抽象部分。本章将通过实践和具体的细节来把第1章中讲述的理论概念落到实处。特别是, 本章将会使用HelloWorld示例应用程序来展示基本的Suts2架构组件。这个示例应用程序并不能 展示框架的全部功能。正如我们说的那样,本书中将开发一个全功能的示例应用程序一Suts2 公文包应用程序。HelloWorld应用程序的目的仅仅是建立一个能运行的Struts2应用程序。 在开始HelloWorld应用程序之前,我们需要了解一下配置Struts2应用程序的基础知识。特 别是,我们会介绍一种被称为声明性架构(declarative architecture)的配置方式。 2.1声明性架构 在本书中,我们使用声明性架构这个短语来引用一种应用程序配置,这种配置允许开发人员 在较高级别描述应用程序的架构,而不是直接编程操作。这种方式很像HTML文档只描述它的组 件,而将创建这些运行时实例的任务交给了浏览器。Struts2允许你通过高级的声明性架构特性米 描述应用程序的架构组件,而将运行时创建这些架构组件的任务交给框架。在本节中,我们将会 了解上述过程的细节。 2.1.1两种配置 首先,我们需要澄清一些术语。在本章的介绍部分,我们把声明应用程序的Sus2组件的行 为称为配置(configuration)。虽然这么命名没有什么错误,但是它可能会引发一些困感。这个在 意义深远的配置概念之后,在Suts2项目内部我们可以区分两类明显不同的行为。其中一类是声 明性架构,与实际构建Struts2应用程序更接近,而另外一类在本质上更接近于管理。从概念上区
2.1声明性架构17 分这两种配置非常重要。 1.配置框如 首先,我们讲解传统意义上的配置:它们更接近于管理活动。由于Suts2框架非常灵活,所 以可以从很多方面调整它的行为。如果你想改动框架赖以识别哪些请求应该处理的URL扩展名, 2 那么可以配置框架去识别你所用的扩展名。默认情况下,Struts2查找以.action结尾的URL,但 是你可以配置框架查找.o(Sus1的默认扩展名),甚至可以不使用任何扩展名。其他配置参 数还包括上传文件的最大容量以及开发模式标志等。由于这些配置属于管理性质,我们会在本书 中遇到这些配置的主题时再讲解。 现在,我们将关注如何构建web应用程序。 2.声明应用程序的架构 这种类型的配置更重要,我们称之为声明性架构,包括定义应用程序使用的Struts2组件,以 及将它们连接起来形成需要的工作流程路径(workflow path)。这里说的工作流路径是指当一个 特定的UL被单击时会触发哪个动作,以及该动作会选择哪个结果来完成处理流程。 定义声明性架构是一种特殊的配置方式,它允许开发人员以描迷而不是硬编码千预的方式创 建应用程序的架构。开发人员使用高级工件(artifact)来描迷架构组件,例如,XML文 件或者Jva注解,系统会利用这些高级工件创建应用程序的运行时实例, 开发人员只需要声明哪些对象作为应用程序的动作(action)、结果(result)以及拦截器 (interceptor)。这个声明过程主要包括指定哪个Java类实现哪个接口。几乎所有Struts2架构组件 都被定义为接口。实际上,框架提供了你可能会用到的几乎所有组件的实现。例如,框架包含 很多结果的实现以支持不同类型的视图层技术。通常情况下,开发人员只需要实现动作,之后将 它们与内建的结果和拦截器关联起来即可。此外,使用智能默认值和注解可以进一步减少配置过 程中所需要的手工任务。 2.1.2声明架构的两种方式 现在我们来看看声明架构的细节。声明应用程序的架构有两种不同的方式:通过基于XML 的配置文件或者通过Java注解。图2-1展示了声明性架构的双重接口。 如图2-1所示,不管使用ML方式还是Java注解方式声明应用程序的Suts2组件,框架都会 将它们转化为相同的运行时组件。在使用XL的情况下,我们使用带有描述应用程序的动作、 结果以及拦截器等元素的XML配置文档。在使用Java注解的情况下,没有XML文件。所有的元 数据都集中放在Java注解中,这些注解直接驻留在实现动作的类对应的Java源代码中。不管你使 用哪种方式,框架都会产生相同的运行时应用程序。你可以使用自己喜欢的配置方式,它们没有 功能上的优先顺序,从这种意义上来说两种机制是冗余的。声明性架构是这里的真实概念。选择 使用哪种风格的声明主要取决于你的偏好。现在让我们看看每一种候选方式,以及它们分别是如 何工作的