Apache Geronimo 和 Spring 框架,第 4 部分: 混合使用 Spring AOP 和 Spring Web Flow
2010-04-16 00:00:00 来源:WEB开发网开始之前
本系列教程适合于需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 应用服务器上使用 Spring 框架的强大功能的 Java™ EE 开发人员。
关于本系列教程
本系列教程共分为 6 个部分,向您介绍了 Spring 框架及 Spring 框架怎样与 Geronimo 结合使用。我们将从检验各种 Spring 框架方法及其怎样与 Geronimo 服务器结合使用入手。在整个系列教程中,您将开发和部署个人电话本应用程序。该应用程序包括以下功能:
显示电话本
显示每个条目的详细信息
向电话本中添加一个新条目
编辑、修改和删除条目
向条目中添加更多详细信息,例如主电子邮件地址
第 1 部分 介绍了 Spring 框架的各个模块,并介绍了每个模块与在 Geronimo 应用服务器上开发 Java EE 应用程序的关系。该部分还说明了 Spring 框架所基于的方法。
第 2 部分 介绍了如何使用 Spring 框架在 Geronimo 上构建第一个骨架系统应用程序。
在 第 3 部分 中,您将采用通过 Apache Derby 数据库添加 Java 数据库连接 (JDBC™) 支持来扩展在第 2 部分中获得的 Geronimo 应用程序。您还将了解如何将对象关系映射集成到使用 iBatis 的应用程序中。
在第 4 部分中,您将面对 Spring 面向方面编程 (AOP) 和 Spring Web 框架。使用 Spring AOP,任何受 Spring 框架管理的对象都可变为面向方面的,并且本教程利用了通过 Spring AOP 提供的声明式事务管理服务。
第 5 部分介绍了 Spring Model-View-Controller (MVC)。该教程向您介绍了 Spring MVC 框架及 Web 视图,使您可以了解 Spring MVC 的入门知识。
在本教程结束的第 6 部分,介绍了如何通过 Spring 框架使用 JavaServer Page™(JSP™)、Velocity、Tile 和 PDF 导出功能。您将使用和体验 Spring MVC 内置的各种 Web 视图。
关于本教程
在 第 3 部分 中,扩展了电话本应用程序,使其从 Derby 数据中动态读取数据;并且您看到了动态更改数据源而不碰一行代码是多么简单。使用了 Spring 框架提供的 DAO 和 ORM 模块来扩展应用程序,并且还了解了如何将其部署到 Geronimo 上。
在本教程中,将通过引入事务把应用程序带入到下一个级别,事务是一组可恢复的动作,其保证的输出要么是采取了所有动作,要么是未采取任何动作(如 “漫游 Web 服务原子事务操作” [developerWorks,2004 年 9 月] 文章所述)。Spring AOP 模块是随 Spring 框架分发预先打包而来的,并且事实上它也是核心模块之一。您将看到 Spring 如何支持方面、连接点、建议和切入点等 AOP 概念,然后再将把这些概念引入应用程序中。您将向应用程序中添加一个新页面以添加新条目,从而把事务引入到应用程序中。
您还将了解如何使用 SWF 模块明确定义应用程序的流程。随着应用程序复杂度的增加,管理页面的浏览流程就变得更难,而 SWF 能够缓解这种痛苦。
先决条件
本教程假定您熟悉面向对象的编程 (OOP) 并且熟知 J2EE 和 Java EE 术语。了解面向方面编程更佳,但不作硬性要求。但是,您必须熟悉 AOP 基本概念并了解基本的 XML 语义。本教程涉及到了一些 AOP 概念并会尝试简要加以介绍,但您的主要目的是要了解 Spring AOP。
系统要求
您的系统需要至少满足以下要求才能继续学习本系列教程
The Spring Framework, Version 1.2.8 —— 具有所有依赖性的压缩文件。
Apache Geronimo 1.1 —— Geronimo 是 Apache 的 J2EE 认证应用服务器。
Spring Web Flow —— SWF 是 Spring 框架的一个独立模块,并且需要单独下载。本教程使用版本 1.0-rc3。
Apache Derby 数据库 —— 本教程使用 Derby,该数据库是开源的轻量级数据库。Derby 是嵌入到 Geronimo 1.1 里的,因此不需要再单独安装。
Apache Ant —— 确保正确配置 Ant 并且其 /bin 目录位于 Path 系统变量中。
Java 1.4.2 —— 确保 Java 安装并运行在系统中。
以下是安装和配置开发、部署和运行示例应用程序所需的软件的指导信息。
Spring 框架和 Geronimo 安装 —— 为了使样例代码能够运行,需要安装运行 Geronimo 和 Spring 框架。有关安装指南,请参阅 第 2 部分。
Spring Web Flow —— SWF 模块是一个独立的 API。您将使用版本 1 的发行测试版 3(SWF 版本 1-rc3)。将需要使用此次下载附带的 JAR 文件。下载名为 spring-webflow-1.0-rc3.zip 的压缩文件,并将其保存到硬盘某个位置。下载完后,将压缩文件解压缩到应用程序所在的驱动器中(例如,我把它安装到了驱动器 K: 中)。解压缩该文件应当会创建一个名为 spring-webflow-1.0-rc3 的目录(在我的驱动器中,SWF 的安装目录是 K:\spring-webflow-1.0-rc3)。此时,您不需要为 SWF 做任何配置。
Derby 数据库 —— Geronimo 1.1 预打包附带的 Derby 数据库安装不需要任何特殊配置。您将使用在 第 2 部分 中创建的表,因此请参考该教程以获得创建及填充本教程所需的数据库表的步骤。
Spring AOP 和 Spring Web Flow 简介
在此部分中,将从更深层次向您介绍 AOP、Spring AOP 模块和 Spring SWF 模块。
AOP 的基础知识
AOP 是 Spring 框架的主要组件之一,它基于把横切性对象分离为关注点或方面的原理。如 Graham O'Regan 在其文章 “Introduction to Aspect-Oriented Programming”是这样解释的:“面向方面编程 (AOP) 是面向对象的 (OO) 编程的补充,它允许开发人员动态地修改静态 OO 模型以创建一个可以不断成长以满足新需求的系统。就像现实世界中的对象可以在其生命周期内更改状态一样,应用程序也可以随着开发过程的深入而采用新特性。”
几乎所有现实的软件系统都有若干个关注点,这些关注点横跨了多个模块。实现这些关注点的面向对象的技术并不总是能提供最理想且清晰的解决方案。AOP 方法可以减轻这种痛苦,它允许将此类关注点设计为方面。横切性关注点的最简单示例是日志记录和事务。这两个关注点可以跨越多个模块并且使用 OO 设计方法,您可能会因为要为每个模块定义事务管理而感到有些麻烦,也可能会在使用单个事务模块时遇到同步问题。通过使用 AOP 方法,您可以将其定义为方面,这些方面可以被任何需要使用该方面的模块共享,并且不需要担心管理或事务问题。
下面是 AOP 的一些核心概念:
方面 (Aspect):方面是可影响其核心关注点的编程模块的一部分。它类似于 OO 术语中的类。日志记录和事务都是 J2EE 和 Java EE 应用程序中的横切性关注点的示例。
联结点 (Join point):定义为程序执行时主程序和方面相遇的任何逻辑点。此类联结点的示例包括方法调用和异常。在 Spring AOP 中,一个联结点总是一次方法调用。
建议 (Advice):在 AOP 术语中,建议被定义为一项应当在特殊联结点上采取的操作。这些操作可以是方法调用、过程或抛出异常。在 AOP 中有各种建议,其中 before、after 和 around 是三个主要类型。
切入点 (Pointcut):指定建议何时应当触发的联结点集合。Spring AOP 允许将切入点定义为正则表达式。
目标对象 (Target object):被建议的对象并且包含联结点。
AOP 代理 (AOP proxy):由 AOP 框架创建的对象,用于实现 AOP 关注点,包括建议。在 Spring AOP 中,这可以是 JDK 动态代理或 cglib 代理。
织入 (Weaving):定义为聚合所有方面以创建被建议的对象。Spring AOP 在运行时执行织入。
不同的建议类型包括:
Around 建议:这是围绕联结点的一种建议。在 AOP 中,联结点通常是方法调用。它可以控制程序的执行,并且可以定义逻辑判断执行是应当继续执行,还是应当采取另一条路径。这种控制功能使得此建议被广泛应用到了 AOP 应用程序中。
Before 建议:这是在联结点之前执行的建议。但是,它没有像 Around 建议一样拥有的对程序流程的控制。
After 建议:类似于 Before 建议,只不过在这种情况下是在联结点正常执行完后才执行该建议。这种建议也不能像 Around 建议一样可以控制流程。
Throws 建议:Spring AOP 允许在方法(联结点)抛出异常时定义此类特殊建议。
Spring AOP
上一部分介绍了几种基本的 AOP 术语。现在,让我们来看看 Spring AOP 中的等效术语。Spring 框架目前支持 AOP 的概念集,范围从切入点到建议。
使用 Spring 作为建议程序或拦截程序来实现方面。
在 Spring AOP 中,联结点始终是方法调用。
Spring AOP 为作为拦截程序对象的所有 AOP 建议提供了实现。
Spring AOP 支持使用正则表达式声明性地定义切入点。
在 Spring 中,AOP 代理将是 JDK 动态代理或 cglib 代理。
图 1 显示了 Spring AOP 如何支持此处定义的概念。
图 1. Spring AOP 中的 AOP 概念
使用示例电话本应用程序,您将看到如何使用 Spring AOP 来定义建议、切入点和建议程序等 AOP 概念。示例还将使用依赖性插入,向您展示如何将这些方面动态插入对象中。
然后介绍由 Spring 提供的事务管理。事务管理将在内部使用 Spring AOP 以实现其目标。最后将扩展电话本应用程序声明性地使用此事务。
Spring Web Flow
根据 Spring 框架的官方文档,“SWF 是专注于在 Web 应用程序内定义用户界面流程的产品”。换言之,SWF 将以一种高度可配置且可重用的方式定义此流程。系统允许捕捉独立工作流程并在各种环境下加以重用。
用 SWF 定义工作流程
下面是使用当前技术定义工作流程会遇到的一些问题:
当前技术处理小型且较简单的导航工作流程很轻松,但随着 Web 应用程序变得越来越大越来越复杂,流程管理开始四分五裂。
不能在各种环境中轻松地重用工作流程,因为流程都是写死的。
Spring SWF 通过允许在配置文件中声明性地定义工作流程来尝试解决这些问题。这样您就可以在各种场景中使用这些工作流程。当 Web 应用程序具有一个像向导一样的流程时,这最有帮助。
在本教程的稍后部分,将使用 SWF 声明性地定义电话本应用程序的流程。还要把在 AOP 部分中定义的方面与 SWF 整合到一起来演示 SWF 的完整功能。
用 Spring AOP 扩展电话本应用程序
在此部分中,将讨论到如何将 Spring AOP 与一个方法标记的方面结合使用。应用程序将使用建议,并会给出定义为方法标记程序或跟踪程序的切入点。还会介绍建议程序中调用了哪些方法以及每个建议程序类中的执行流程。稍后事务也将被引入到此应用程序中,您将看到如何声明性地定义应用程序的流程。所有工作都将在 XML 文件中完成。
这两个版本的应用程序都将使用在 第 3 部分 中定义的 Spring JDBC 类以供数据库调用。
工作区的目录结构
图 2 展示了应用程序的布局。下载本教程附带的源压缩文件并将其解压缩到硬盘驱动器中。
本教程涉及到了此处所示的所有目录及文件。
图 2. 解压缩源文件后得到的应用程序目录结构
定义 Add New Entry 页面
需要做的第一件事是为应用程序定义 Add New Entry 页面。该页面将在一张表单中包含构成电话本条目的所有字段。此表单上的提交操作将把一个新条目添加到电话本数据库中。
完成此操作后,将在应用程序中引入和定义方法标记的方面。如先前所述,方法标记程序方面可被视为方法跟踪的方面。基本想法是通过在每个联结点处放置有意义的消息来理解应用程序中的 Spring AOP 执行流程。
将所支持的类用于 Spring AOP 支持的建议是十分直观的。了解方面的一种方法是在应用程序中定义方法标记的方面。虽然它的功能不是很多,但却展示了 AOP 的强大。
下一步将是把另一个方面 —— 事务 —— 引入应用程序中。Spring 将通过允许您在应用程序中声明性地定义此类事务,再次展示其威力和简单性。您不需要更改任何代码。只需对此事务声明性地循环执行数据库调用,Spring 将在成功时把事务提交到数据库,或在满足任意一个已定义的失败条件时将事务回滚。
改进当前的电话本应用程序
图 3 展示了 Geronimo 中的事件流程,第 3 部分 中所述。
图 3. 如何使对 home-aop.jsp 的请求在 Geronimo 应用服务器上得到处理
现在来看一看 Spring AOP 支持的三种不同的建议。
事件的 Before 建议流程
此处要为标记方面定义一个用于实现 MethodBeforeAdvice 的类。MethodBeforeAdvice 是 Spring AOP 提供用于实现 AOP 的 Before 建议结构的一个接口类。需要实现此类的 before() 方法,并且 Spring AOP 框架将确保在实际的方法调用前在运行时先调用此方法。下一部分中的顺序图可以更清晰地说明此问题。
另外,还将在 ApplicationContext.xml 文件中引入 Bean 以将此建议应用到目标对象 PhonebookDataProvider 中。此 Bean 还将为 Spring AOP 定义切入点以将此建议应用到目标对象的所有方法中。
Spring 使用代理模式为基于 ApplicationContext.xml 文件中所做的配置的应用程序提供 AOP 工具。
图 4 概括了执行 Before 建议时 Spring 容器里的事件序列。
图 4. 定义 Before 建议后的事件序列
如您在 图 4 的序列图中所见,SpringAOProxy 是 ProxyFactoryBean 的一个实例,它负责在切入点处截取执行,然后将特定的建议应用到目标对象上。在这种情况下,首先将在 Spring AOP 的 BeforeAdvice 类的实现中调用 before() 方面。然后通过在 PhonebookAdapter 类中调用实际的 getPhonebookEntries() 方法来结束。
事件的 After 建议流程
类似于 Before 建议,此处将定义一个可以实现 Spring AOP 提供的 AfterReturningAdvice 接口的类,以实现 AOP 的 After 建议结构。
图 5 中描绘的事件流程与 Before 建议的基本一样,惟一的不同是目标对象中的实际方法是在应用 After 建议之前调用的。
换言之,先调用 getPhonebookEntries() 方法,然后 SpringAOProxy 调用 afterReturning() 方法。
图 5. After 建议的事件序列
事件的 Around 建议流程
这是 AOP 中最有趣且使用最为广泛的建议。此建议与其他两种建议之间的主要差别在于在实现此建议时可以实施更多控制,因为执行流程将停在由 Advice 类实现的 invoke() 方法中。这就是此建议被称为 主动 建议,而其他建议被称为 被动 建议的原因。
Around 建议专门用于在调用前后实现自定义行为。它们负责选择是返回自己的返回值继续执行操作,还是抛出异常切换到其他执行路径。
Around 建议在 Spring AOP 中被定义为拦截程序。拦截程序是拦截方法并抛出异常以取得程序执行控制的类。图 6 展示了如何为应用程序实现 Around 建议。
图 6. Around 建议的事件执行
从图中可以看到,当 Spring 看到一个 Around 建议时,它将在 MethodInterceptor 接口(实现此 AOP 结构的接口)的实现上调用 invoke() 方法。只需注意必须在 MethodInvocation 对象上显式调用 proceed() 方法,以在目标对象上执行该方法。
如果对应用程序开发进行更深层次的钻研,以上定义的所有流程将会更有意义。
插入 Spring AOP 支持的事务管理
应用程序的下一个逻辑步骤是将事务管理引入其中。
声明性事务管理特性是由 Spring 框架中的 Spring AOP 启用的。虽然此特性使用 AOP,但它非常明晰以至于用户不需要了解 AOP,并且无需更改任何代码就可以将事务引入到应用程序中。
这里将使用 TransactionProxyFactoryBean 来创建事务性代理以将事务方面引入应用程序。此 Bean 只是 Spring 的一般 ProxyFactoryBean 的一特例。此 Bean 还将自动创建 TransactionInterceptor 并将其与该代理对象连接起来。
然后,将在必须应用此事务方面的方法上声明性地定义切入点。该定义有三个必须指定的主属性:
transactionManager:要使用的 PlatformTransactionManager 实现(例如,DataSourceTransactionManager 实例)
target:应为其创建事务代理的目标对象
transactionAttributes:每个目标方法名称(或方法名称模式)的事务属性(例如,传播行为和 readOnly 标志)
应用程序将使用 Spring 框架附带的 JDBC DataSourceTransactionManager。target 是服务对象 PhonebookDataProvider 的接口定义。此处将为应用程序的 add 方法定义事务属性。
事务性代理将实现目标的接口;在本例中,为 PhonebookDataProvider 类。这是为此类定义接口的主要原因。
应用程序的数据模型定义和数据库设置
在尝试访问数据库前,必须确保数据库已正确设置。这里将使用在第 3 部分中创建的同一个数据库。因此如果遵循本系列教程的 第 3 部分 中的指导信息进行了操作,则应该已全部设定完毕;如果没有遵循上述指导信息,则必须返回并完成第 3 部分,然后才能继续本教程。
准备好数据库中的数据后,可以继续进行应用程序处理。根据先前部分中的建议,需要先定义 Add New Entry 页面。然后从最简单的方面 —— 方法标记(或方法跟踪)—— 开始,接下来把事务声明性地添加到应用程序中。稍后您将看到如何使用 SWF 声明性地定义相同的导航流程。
定义 Add New Entry 页面
需要使用此页面将导航流程添加到应用程序中以及显示 Spring 框架的事务管理特性。此页面的 JSP 非常简单,只有一张表单和几个动作。您可以在 <WORKSPACE>/ phonebook\web\WEB-INF/jsp 目录中查看这段代码。图 7 显示了运行此页面在浏览器中会看到的一个结果画面。当您在应用程序主页中单击 Add an Entry 按钮时将调用此页面。
图 7. 应用程序的 Add New Entry 页面
此时,所需的应用程序的视图部分就准备好了。下一步要做的是引入方面。
引入 AOP 方面
在此部分中,将定义接口并将涉及 Before 建议、After 建议和 Around 建议类。然后将构建、部署并在 Geronimo 中运行电话本应用程序。
在电话本中实现跟踪方面
用于定义方面和事务管理的 AOP 代理将实现目标对象的接口:PhonebookDataProvider 。因此就需要为服务类定义接口。
回想一下在 第 3 部分 中定义的 PhonebookDataProvider 类。该类用作客户机使用的服务类,并且完成了制作数据库连接和将结果返回给客户机的所有苦活。该类由 JSP 实例化以从应用程序中获取数据。将同样使用这个类引入跟踪方面,但由 Spring AOP 与接口协作。因此需要完成的第一项工作是为此类编写接口。
清单 1 显示了 IPhonebookDataProvider 接口。该接口中包含了将要开发的应用程序部分所需的所有方法。JSP 将调用 getEntries()。
清单 1. 应用程序的 PhonebookDataProvider 接口
/**
* IPhonebookDataProvider.java
* This class defines the methods to be used by the PhonebookDataProvider class.
*
* @author Arun Chhatpar
*/
public interface IPhonebookDataProvider {
public List getPhonebookEntries() throws Exception;
public PhonebookEntry getPhonebookEntry(int entryID, int rowID) throws Exception;
public int addEntry(PhonebookEntry pe) throws Exception;
}
此类没有多少代码,只有方法定义。有了接口之后,就可以让服务类来实现该接口。
PhonebookDataProvider 实现新接口
PhonebookDataProvider 将实现新定义的接口。此处的大部分函数与在 第 3 部分 中为应用程序定义的函数相同。请注意,有一个新方法用于支持 Add new PhonebookEntry 功能。
清单 2. PhonebookDataProvider 定义所有方法并实现 IPhonebookDataProvider 接口
public class PhonebookDataProvider implements IPhonebookDataProvider{
private IPhonebookDAO pbDao;
public PhonebookDataProvider() {
}
public void setPbDao(IPhonebookDAO pbDao) {
this.pbDao = pbDao;
}
public List getPhonebookEntries() throws Exception{
return pbDao.getPhonebookEntries();
}
public PhonebookEntry getPhonebookEntry(int entryID, int rowID) throws Exception{
return pbDao.getPhonebookEntry(entryID, rowID);
}
public int addEntry(PhonebookEntry pe) throws Exception{
return pbDao.addEntry(pe);
}
}
引入方法标记的方面
方法标记的方面将捕捉所有对被标记方法的调用及从其返回的结果。我们将在这些联结点处显示一条消息。如在 AOP 的基础知识 部分中的定义,有三类建议:Before、After 和 Around。Spring AOP 有支持全部三类建议的接口。我们将定义三个类用于实现这些接口并在每个接口中放置标记程序以查看运行情况。
Before 建议
MarkingBeforeAdvice 类用于为本 AOP 示例定义 Before 建议。它将实现 MethodBeforeAdvice 接口。下面是从该接口实现 before(...) 方法的代码。
清单 3. 实现 Before 建议的示例
public class MarkingBeforeAdvice implements MethodBeforeAdvice
{
public void before(java.lang.reflect.Method method, Object[] obj, Object obj2)
throws Throwable {
Calendar cal = Calendar.getInstance();
System.out.println(cal.getTime() + " :::: PREPARING TO CALL "+method.getName()+"
of "+obj2.getClass().getName()+"");
}
}
这个 Advice 类定义用于指定实现 before() 方法以启用 Spring 运行时在达到适当的联结点时通报建议。
必须让 Spring 应用程序上下文识别到此建议,还要将此建议附加到应用程序的适当联结点上。清单 4 显示了实现上述操作的配置。
清单 4. 向应用程序上下文中添加 Before 建议
<bean id="beforeTracer"
class="phonebook.aop.MarkingBeforeAdvice"/>
<!-- Bean Definitions for Advisors -->
<!-- Before Marking Advisor -->
<bean id="beforeMarkingAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="beforeTracer"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<bean id="phonebook"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>phonebook.dao.IPhonebookDataProvider</value>
</property>
<property name="target">
<ref local=" phonebookJdbcDataProvider"/>
</property>
<property name="interceptorNames">
<list>
<value>beforeMarkingAdvisor</value>
</list>
</property>
</bean>
此处的第一个 Bean 定义用 Before 建议接口的实现定义一个新 Bean。
第二个定义将定义 before 标记建议程序。它定义了切入点及必须在每个切入点上执行的建议。可以使用正则表达式为应当应用建议的选定方法设定模式。我仅使用了 .* RE 将此应用到目标对象的所有方法中。
使用 ProxyFactoryBean 的最后一个 Bean 是起整合作用的实际 Bean。ProxyFactory 在接口上运行,并且它将仅把此方面应用到实现在此属性中定义的接口的类中。目标用于指定方面应当围绕的对象。
After 建议
After 建议的定义与上述类似;开始先定义一个用于实现 AfterReturningAdvice 接口的类。afterReturning() 方法将在目标对象上的实际方法执行完后在运行时被拦截。
可以在 src 目录中查看类和定义。
Around 建议
添加 Around 建议的过程与上面说明的 Before 建议定义十分相似,只有一处重要区别。由于 Around 建议在 Spring AOP 中是作为拦截程序被实现的,因此实现类将实现 MethodIntercepter 接口。
另一个重要区别在于通过在 MethodInvocation 对象上调用 proceed() 方法会明显延长执行流程。如果不这样做,则 Around 建议方面将不在目标对象上调用方法。这种设计为开发人员提供了定义实际方法被调用后的前操作和后操作的能力。
清单 5 展示了 Around 建议类的定义。
清单 5. 实现 Around 建议的类
public class MarkingInterceptor implements MethodInterceptor {
public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation)
throws Throwable {
Calendar cal = Calendar.getInstance();
System.out.println(cal.getTime() + " :::: NOW INVOKING
"+methodInvocation.getMethod().getName()+".");
Object retVal = methodInvocation.proceed();
cal = Calendar.getInstance();
System.out.println(cal.getTime() + " :::: FINISHED INVOKING
"+methodInvocation.getMethod().getName()+".");
return retVal;
}
}
您可以看到此类与压缩文件附带的可部署的 .war 文件协作,也可以按照以下说明构建和部署该文件。
构建、部署和运行
本教程附带的源压缩文件配有所有类、配置文件和 Ant 构建文件(如果需要构建)。压缩文件中还有一个配有全部所需内容的可部署的 .war 文件。可以使用任何一种方法来获取 phonebook.war 文件。
还必须确保 <WORKSPACE>/phonebook/lib 目录中包含 readme.txt 文件中提及的所有 JAR 文件。请仔细阅读 readme.txt 文件中的说明并确保将所有必需的文件都复制到 <WORKSPACE>/phonebook/lib 中。
注: 可以参考本系列教程的 第 2 部分 中的构建和打包说明。
使用 Geronimo 中的 Deploy New 工具部署 phonebook.war。如果一切运行正常,将在 Geronimo Web Console 上看到一条消息,说明 Phonebook application deployed successfully。
现在,将浏览器指向新页面:http://localhost:8080/phonebook/home-aop.jsp。
如果一切都按预期运行,主页应当如 图 8 所示。
图 8. 运行在应用服务器中的 home-aop.jsp
应当会在 Geronimo Console 上看到系统消息,说明正被执行的所有建议。
刚刚看过了在应用程序中运行的跟踪方面。现在来看看如何通过在应用程序上下文文件中添加几个 Bean 轻松地声明性地引入事务管理方面。
用几个 Bean 添加事务
是的,这就是将事务方面插入应用程序所需完成的全部工作。请参阅 清单 6 中的这些 Bean。
清单 6. 事务管理 Bean
<!-- Bean Definitions required for Transactional Support -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pbJdbcDataSource"/>
</bean>
<bean id="phonebookTxn"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
>
<property name="transactionManager" ref="txManager"/>
<property
name="proxyInterfaces"><value>phonebook.dao.IPhonebookDataProvider</
value></property>
<property name="target" ref="phonebookJdbcDataProvider"/>
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
第一个 Bean 用于定义管理事务时必须使用的事务管理程序。由于需要包装 JDBC 事务,因此使用 Spring JDBC 模块中的 TransactionManager 实现。它配有应用建议所需的方法拦截程序。
在本教程的先前部分中讲解过 TransactionProxyFactoryBean。现在是时候来查看一下它是如何实现事务所需的核心功能的。并且如您所见,您可以向它插入任何事务管理程序并使其相互协作。需要定义一个目标对象,在这个目标对象上事务应该被应用到目标属性内。将使用在 第 3 部分 中定义的 phonebookJdbcDataProvider Bean 实现此操作。在那里还可以学到定义此 Bean 和其他 Bean 向 Derby 数据库读取和写入数据。
最后而且也是最重要的一步,定义 transactionAttributes。事务属性包含需要使用哪些事务语法的定义。此处就是 把事务应用到以 add 为开头的所有方法中。PROPAGATION_REQUIRED 表示支持当前的事务或创建一个新事务(如果没有)。那一行中的最后一个条件告诉事务管理程序在出现异常时回滚事务。减号表示回滚;如果在出现异常时仍要提交,则使用加号(+)。
这就是将事务引入应用程序所需完成的全部工作。
构建并运行
全部完毕。运行 Ant 来构建并创建 WAR 文件。使用 Geronimo 中的 Deploy New 工具部署该文件。
让浏览器指向新页面:http://localhost:8080/phonebook/home-aop.jsp。
将获得同样一个主页,但这一次它是围绕事务的。应当亲自尝试添加并查看新条目。下面是一些用于测试事务管理程序的提示:
在 Add New Entry 页面中添加所有字段;应当会看到所有信息都返回到主页列表中。
不输入名字或姓氏。将会看到一条错误消息,并且在后端,事务会被回滚。可以通过查看主页或数据表本身加以验证。
最后也是最重要的一个测试是添加名字和姓氏并将其他所有字段留空。将会再次看到错误消息,事务也将会被回滚。但如果还记得数据库表定义和 AddEntry() 方法的 SQL 语句,就可以使用它们把数据插入第一张表中 —— FirstName 和 LastName,然后把其余数据添加到第二张表中。有两次插入操作,因此如果在第二次插入时出现异常,事务管理程序将回滚最后一次提交的 SQL 语句。
至此 AOP 就全部介绍完了。完成了向应用程序中定义并添加两个方面,现在将要添加 SWF。
Spring Web Flow
SWF 系统允许捕捉 UI 流程作为独立模块。这些模块可在各种系统中重用。这里将改用 SWF 来定义应用程序的导航流程。
在 SWF 中,每个流程都是一个有限状态机 (FSM),其中包含状态和转换。状态表示执行一些动作或显示给用户以供用户输入的视图。开始先定义 phonebookFlow.xml 文件。此文件将包含流程定义。
注:在流程定义中将不定义动作;那是高级主题。
然后将对 JSP 表单做一些小改动,并查看其运行情况。
添加 SWF
现在可以将静态流程和硬编码流程改为使用 SWF 声明性地定义。可以通过三个步骤来实现:
在 XML 文件中定义应用程序的 Web 流程。
定义 XMLFlowFactory Bean 在 ApplicationContext 中访问此流程。
执行此流程。
第一步是在 XML 文件中定义流程。清单 7 显示了用于本应用程序的这一流程。
清单 7. PhonebookFlow.xml 为应用程序定义流程
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "-//SPRING//DTD WEBFLOW 1.0//EN"
"http://www.springframework.org/dtd/spring-webflow-1.0.dtd">
<flow start-state="showPhonebook">
<!-No Action Processing plugged in so far. Just getting the flow in place first
-->
<view-state id="showPhonebook" view="home-dao">
<transition on="goToAddEntry" to="addEntry">
</transition>
</view-state>
<view-state id="addEntry" view="addentry">
<transition on="goToHome"
to="showPhonebook"></transition>
<transition on="saveEntry" to="addEntry"></transition>
</view-state>
</flow>
此配置文件是不言自明的。此 XML 中的顶部条目用于为应用程序定义启动状态。然后定义 showPhonebook 状态的视图状态,然后再去定义 addEntry 状态。视图属性定义用于保留特殊视图定义的 JSP。
流程部署
在 清单 7 中,看到了定义流程是多么容易。现在来看一看如何在特殊环境(例如 Spring MVC)内部署该流程(参见 清单 8)。
清单 8. 部署此流程所需的配置
<bean id="flowRegistry"
class="org.springframework.webflow.registry.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="/WEB-INF/phonebookFlow.xml"/>
</bean>
<!-- Creates, saves, and restores managed flow executions -->
<bean id="repositoryFactory"
class="org.springframework.webflow.execution.repository.continuation.ContinuationFlow
ExecutionRepositoryFactory">
<constructor-arg ref="flowRegistry"/>
</bean>
<!-- Launches new flow executions and resumes existing executions. -->
<bean id="flowExecutor"
class="org.springframework.webflow.executor.FlowExecutorImpl">
<constructor-arg ref="repositoryFactory"/>
</bean>
<bean name="phonebookFlowController"
class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
<property name="cacheSeconds" value="5"/>
</bean>
此处有两个主要的 Bean:第一个 Bean,用于定义流程定义文件的位置;最后一个 Bean,用于将该 Bean 提供给 FlowController 实现。MVC FlowController 配有在执行时将此配置转换为流程的所有逻辑。
在 JSP 页面中做一些小改动
在 addEntry.jsp 页面的以下部分中最重要的部分是 _flowExecutionKey 输入参数。此键是由 SWF 生成的,并且将被作为一个隐藏参数传入参与此流程的每个页面。流程都是有状态的。结果,参与流程的视图必须将 _flowExecutionKey 参数提交回服务器以识别客户机正在参与的流程。此外,视图必须将一张可解析的 _eventId 参数表提交回服务器以通报在按下按钮时发生的事件 —— 例如,当 Submit 按钮被按下时的提交事件。
清单 9 显示了 addEntry.jsp 页面中的这些更改。
清单 9. JSP 页面中的更改
...
<script type="text/javascript">
function doSave()
{
document.myForm.pageAction.value="ADD";
document.myForm._eventide.value="saveEntry";
document.myForm.submit();
}
function goHome()
{
document.myForm. _ eventide.value="goToHome";
document.myForm.submit();
}
...
...
<input type=hidden name=pageAction value=""/>
<input type="hidden" name="_flowExecutionKey"
value="${flowExecutionKey}">
<input type="hidden" name="_eventId" value=""/>
...
需要对 home-dao.jsp 页面做类似更改,将使用该页面作为流程示例的主页。这十分简单。请查看 src 中的 JSP 文件。
构建并运行
就这些!已经全部完成了。运行 Ant 来构建并创建 WAR 文件。使用 Geronimo 中的 Deploy New 工具部署该文件。
需要传入流程定义名称,以便 SWF 可以执行该流程。使浏览器指向此 URL:http://localhost:8080/phonebook/phonebook.htm?_flowId=phonebookFlow。
当单击此 URL 时,SWF 将创建新流程,还将管理新流程!
当部署源文件附带的 .war 文件并指向上述 URL 时,实际上就已经查看到了本教程中讨论的所有技术的运行情况。
至此已实现了本教程设定的所有目标。
Spring 的优点
本教程中涉及的主要技术包括 Spring AOP、声明性事务管理和 SWF。每一项技术都会在 Spring 框架中添加已经备受瞩目的模块集。
下面列出了使用 Spring 声明性事务管理与使用诸如 Enterprise JavaBeans Container-Managed Transactions (EJB CMT) 这类工具相比的优势:
Spring 的事务管理不依赖于任何环境,就好像 EJB CMT 之于 Java 事务 API (JTA) 一样。它可以工作于 JDBC、Java Data Objects (JDO)、Hibernate 或其他有非常少的配置更改的事务。
Spring 的事务管理可以应用到所有传统 Jave 对象 (POJO),而不仅仅是应用到诸如 EJB 之类的特殊类。
Spring 提供了简单的声明回滚规则,这些规则在其他环境中很难实现。
可以在 Spring 中将 AOP 添加到事务顶部。
如您所见,SWF 是另一个功能强大的 Spring 模块。使用 SWF 与使用其他工具相比的优势在于:
应用程序的导航流程是在 .xml 文件中定义的,它清晰可见并且易于理解。
使用 SWF 定义的流程可在其他场景中重用。
Web Flow 的使用合同定义得非常好。它的生命周期清晰、可观察,并被自动管理。简单地说,系统将负责管理复杂性,因此它十分易于使用。
结束语
在本教程中,您了解了 Spring AOP 并尝试了 Spring AOP 所支持的声明性事务管理。AOP 很难理解和实现,但是通过方法跟踪的方面的演示,您可以看到 Spring 框架简化了使用 AOP 的过程。本教程还向您介绍了功能强大的新工具 SWF。
您看到了 Geronimo Web Console 如何使创建及管理数据库变得较为轻松,使用部署工具又如何使部署应用程序变得十分简单。在执行所有这些操作期间,您甚至一次都不需要重新启动服务器。
在第 5 部分中,将探讨最重要的 Spring 模块:Spring MVC。在先前的教程中零零碎碎地看到了此模块的运行,而在第 5 部分中您将进一步深入了解其特性和功能。
您将接触到 Spring MVC 提供的各种控制程序和处理程序。您的应用程序将被扩展以便添加更多功能,包括修改和删除电话本条目。敬请关注!
下载
描述 | 名字 | 大小 | 下载方法 |
第 4 部分的源代码 | geronimo.spring4.source.zip | 119KB | HTTP |
第 4 部分的 WAR 文件 | geronimo.spring4.war.zip | 4150KB | HTTP |
- ››apache设置域名绑定 以及绑定不起作用的排查
- ››apache rewrite将指定URL转向指定的几个服务器
- ››apache配置文件httpd.comf部分参数说明
- ››Apache+Mysql+PHP+phpMyAdmin+Mac OS X 10.7 Lion...
- ››Spring源码学习-含有通配符路径解析(上)
- ››apache+tomcat负载均衡_项目实例
- ››apache mysql php 源码编译使用
- ››Apache添加mod_aspdotnet.so支持ASP.NET配置指南
- ››Apache中改变php.ini的路径
- ››Apache2.2与Tomcat6整合及虚拟主机配置
- ››Apache+php+mysql在windows下的安装与配置图解
- ››Apache+Subversion完美结合,CentOS下实现版本控制...
更多精彩
赞助商链接