Welcome 微信登录

首页 / 软件开发 / JAVA / Java理论和实践: 理解JTS ― 幕后魔术

Java理论和实践: 理解JTS ― 幕后魔术2011-02-04Brian Goetz在这个关于事务的系列文章的 第 1 部分,我们讲述了一些基础知识,包括什么是事务,以及事务对于构建可靠的分布式应用程序来说至关重要的原因。在这一部分,我们将探讨如何将 J2EE 应用程序构建到事务中,以及 JTS 和 J2EE 容器如何设法使事务服务(包括事务划分、资源征用和事务传播)对组件程序员来说几乎是不可见的。请单击文章顶部或底部的 讨论,在 讨论论坛与作者和其他读者分享您对本文的想法。

在这个系列的 第 1 部分,我们讨论了事务并研究了它们的基本属性 ― 原子性(atomicity)、一致性(consistency)、孤立性(isolation)和持久性(durability)。事务是企业应用程序的基本构件;没有它们,几乎不可能构建有容错能力的企业应用程序。幸运的是,Java 事务服务(Java Transaction Service,JTS)和 J2EE 容器自动为您做了大量的事务管理工作,这样您就不必将事务意识直接集成到组件代码中。结果简直是一种魔术 ― 通过遵守几条简单的规则,J2EE 应用程序就可以自动获得事务性语义,只需极少或根本不需要额外的组件代码。本文旨在通过展示事务管理如何发生,以及发生在何处来揭开这个魔术的神秘面纱。

什么是 JTS?

JTS 是一个 组件事务监视器(component transaction monitor)。这是什么意思?在第 1 部分,我们介绍了 事务处理监视器(TPM)这个概念,TPM 是一个程序,它代表应用程序协调分布式事务的执行。TPM 与数据库出现的时间长短差不多;在 60 年代后期,IBM 首先开发了 CICS,至今人们仍在使用。经典的(或者说 程序化)TPM 管理被程序化定义为针对事务性资源(比如数据库)的操作序列的事务。随着分布式对象协议,如 CORBA、DCOM 和 RMI 的出现,人们希望看到事务更面向对象的前景。将事务性语义告知面向对象的组件要求对 TPM 模型进行扩展 ― 在这个模型中事务是按照事务性对象的调用方法定义的。JTS 只是一个组件事务监视器(有时也称为 对象事务监视器(object transaction monitor)),或称为 CTM。

JTS 和 J2EE 的事务支持设计受 CORBA 对象事务服务(CORBA Object Transaction Service,OTS)的影响很大。实际上,JTS 实现 OTS 并充当 Java 事务 API(Java Transaction API)― 一种用来定义事务边界的低级 API ― 和 OTS 之间的接口。使用 OTS 代替创建一个新对象事务协议遵循了现有标准,并使 J2EE 和 CORBA 能够互相兼容。

乍一看,从程序化事务监视器到 CTM 的转变好像只是术语名称改变了一下。然而,差别不止这一点。当 CTM 中的事务提交或回滚时,与事务相关的对象所做的全部更改都一起被提交或取消。但 CTM 怎么知道对象在事务期间做了什么事?象 EJB 组件之类的事务性组件并没有 commit() 或 rollback() 方法,它们也没向事务监视器注册自己做了什么事。那么 J2EE 组件执行的操作如何变成事务的一部分呢?

透明的资源征用

当应用程序状态被组件操纵时,它仍然存储在事务性资源管理器(例如,数据库和消息队列服务器)中,这些事务性资源管理器可以注册为分布式事务中的资源管理器。在第 1 部分中,我们讨论了如何在单个事务中征用多个资源管理器,事务管理器如何协调这些资源管理器。资源管理器知道如何把应用程序状态中的变化与特定的事务关联起来。

但这只是把问题的焦点从组件转移到了资源管理器 ― 容器如何断定什么资源与该事务有关,可以供它征用?请考虑下面的代码,在典型的 EJB 会话 bean 中您可能会发现这样的代码:

清单 1. bean 管理的事务的透明资源征用

InitialContext ic = new InitialContext();
UserTransaction ut = ejbContext.getUserTransaction();
ut.begin();
DataSource db1 = (DataSource) ic.lookup("java:comp/env/OrdersDB");
DataSource db2 = (DataSource) ic.lookup("java:comp/env/InventoryDB");
Connection con1 = db1.getConnection();
Connection con2 = db2.getConnection();
// perform updates to OrdersDB using connection con1
// perform updates to InventoryDB using connection con2
ut.commit();

注意,这个示例中没有征用当前事务中 JDBC 连接的代码 ― 容器会为我们完成这个任务。我们来看一下它是如何发生的。