第21章 Spring事务管理之扩展篇(三)

第21章 Spring事务管理之扩展篇

21.3 Spring与JTA背后的奥秘

无论是Spring的参考文档,还是大多数介绍Spring的书籍,在提到使用Spring的JtaTransactionManager进行分布式事务管理的时候,都强调需要使用从应用服务器的JNDI服务获取的dataSource,而不是本地配置的普通dataSource(见图21-4)。

image-20220627112922236

那么原因是什么呢?我们知道,事务管理是要加之于具体的事务资源上的,所以,通常的PlatformTransactionManager的实现都会有相对应的事务资源的引用,比如,DataSourceTransactionManager需要指定DataSource,HibernateTransactionManager需要指定SessionFactory等。

下方代码清单再次演示了使用DataSourceTransactionManager的情况。

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPoo1edDataSource" destroy-method="close">
	<property name="driverClass" value="$(jdbc.driverClassName)"/>
	<property name="jdbcUrl" value="$(jdbc.url)"/>
	<property name="user" value="$(jdbc.username)"/>
	<property name="password" value="$íjdbc.password)"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

从常识上来讲,像DataSourceTransactionManager这样才是正常的情况。可是,我们也看到,JtaTransactionManager确实没有明确指定依赖于哪个资源,却依然能够将需要加入分布式事务的资源纳入其管理范围。它是怎么做到的呢?如果揭开这个谜团,是否就能搞清楚在使用JtaTransactionManager的时候,要求我们使用从应用服务器的JNDI服务查找到的DataSource的真正原因呢?

在介绍JtaTransactionManager的时候,我们说到,JtaTransactionManager只是对各种具体的JTA实现产品提供的分布式事务管理功能进行了封装,JtaTransactionManager会将最终的工作委派给具体的JTA实现来做。 所以,追根溯源,我们不得不追到JTA规范以及JTA实现中。

略去JTA以及X/Open规范千言不提,我们还是长话短说,直接进入正题吧!

首先,具体的事务资源,RDBMS、MessageQueue等,要加入JTA管理的分布式事务,JTA规范要求其实现javax.transaction.xa.XAResource接口。所以,希望加入JTA管理的分布式事务的RM(ResourceManager,资源管理器)通常会提供相应的适配器(Adaptor),用于提供基于XAResource的分布式事务交互能力,比如关系数据库提供的支持XA的JDBC驱动程序,就是这样的适配器。这样,所有资源管理与事务交互的工作,基本上就由RM的适配器来统一管理了。

在想要参与JTA分布式事务的事务资源拥有了XAResource支持之后,JTA的javax.transaction.TransactionManager(我们称其为JTATransactionManager,区别于Spring的JtaTransactionManager)与RM之间就可以进行通信,如图21-5所示。

image-20220627114711725

适配器通常都有应答能力,这样,在JTATransactionManager使用两阶段提交协议管理分布式事务的过程中,可以同每个RM进行交互。

不过,JTATransactionManager在事务管理过程中要与哪些RM打交道,却不是由它自己说了算的。

想要参与JTA分布式事务的RM何时何地甚至怎样加入JTATransactionManager管理的分布式事务,也不是每个RM自己说了算。

JTATransactionManager与各个RM之间的联系要由ApplicationServer(一般意义上的TPMonitor)来进行协调! ApplicationServer为基于JTA的分布式事务提供运行时环境,并负责协调JTATransactionManager与各RM之间的交互,整个过程类似于如下所叙述的。

(1)ApplicationServer一开始当然要先通过JNDI绑定它的JTA实现中的UserTransaction或者TransactionManager具体实现类,这样,客户端应用程序就可以通过JNDI获取它们。现在客户端应用程序想要开始一个分布式事务,进而UserTransaction或者TransactionManager的相应方法被调用。

(2)ApplicationServer内部会要求TransactionManager为当前事务分配一个唯一的标志(以Xid表示),然后开始事务,并将当前事务绑定到当前线程。

(3)客户端跟ApplicationServer要求相应的数据资源进行数据访问,ApplicationServer会跟RM的适配器要一个事务资源对象,我们暂且称之为TransactionalResource。该资源对象包含两部分,一部分是JTATransactionManager需要与之交互的XAResource,另一部分是要公开给客户端应用程序使用的Connection资源,取得TransactionalResource之后,ApplicationServer要做如下两件事情。

a)ApplicationServer从TransactionalResource中取得XAResource交给TransactionManager。TransactionManager开始通过获得的这个XAResource与RM进行交互。实际上,现在TransactionManager只是调用xAResource的start(xid)方法通知RM开始记录。

b)ApplicationServer然后再把与XAResource属于同一个TransactionalResource的Connection传给客户端应用程序使用,然后客户端应用程序就可以使用ApplicationServer传给的Connection进行数据访问操作了。

(4)客户端应用程序数据访问操作完成,关闭之前ApplicationServer传给的Connection,ApplicationServer在感知到Connection被关闭之后,会通知lTransactionManager,TransactionManager则调用与这个Connection属于同一个TransactionalResource的XAResource的end(xid)方法结束事务记录。如果在当前分布式事务期间还有使用其他RM进行的数据操作,ApplicationServer以几乎同样的方式从RM的适配器那里获取TransactionalResource类似的对象,然后协调TransactionManager重复余下的工作。

(5)当客户端通过UserTransaction或者TransactionManager的相应方法要求结束事务的时候,ApplicationServer就会通知TransactionManager使用两阶段提交协议提交当前事务:

a)TransactionManager调用xAResource的prepare(xid)方法通知各个RM准备提交事务;

b)如果各个XAResource回答全部OK,TransactionManager调用XAResource的commit(xid)方法通知各个RM最终提交事务。

可见,各个RM确实参与到了JTATransactionManager所管理的分布式事务中,只不过,参与的过程由ApplicationServer对客户端应用程序屏蔽了。之所以要求客户端应用程序通过应用服务器的JNDI获取DataSource等资源,是因为只有使用ApplicationServer公开的与XAResource绑定到同一TransactionalResource的Connection,才可以保证客户端应用程序所做的所有数据访问操作能够加入ApplicationServer所协调的分布式事务中(如图21-6所示)。

image-20220627120129304

ApplicationServer为客户端应用程序公开与当前分布式事务相关的Connection的方式,就是实现一个DataSource,然后把该DataSource绑定到JNDI。这样,客户端应用程序就可以通过从JNDI取得的DataSource中获取与事务相关的Connection了。 如果使用本地定义的DataSource,因为它与当前分布式事务不发生任何关系,所以,也就根本不可能参与到分布式事务中去。

不过,如果我们使用的JTA实现不是相应的ApplicationServer提供的,比如,可以独立使用的Atomikos或者JOTM等JTA实现,要求我们从应用服务器的JNDI服务取得相应的DataSource这一前提也是不成立的。这时,我们直接使用各个JTA产品提供的DataSource封装类进行数据访问即可,与ApplicationServer屏蔽掉RM与TransactionManager之间的关系一样,这些产品也有与ApplicationServer完成同样工作的角色,为我们关联具体的RM与当前产品的TransactionManager。

下方代码清单中是在Spring中使用Atomikos的典型配置方式。

<bean id="datasource1" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
	<property name="uniqueResourceName" value="XADBMS_ONE"/>
	<property name="xaDataSourceClassName" value="COM.FirstSQL.Dbcp.DbcpXADataSource"/>
	<property name="xaDataSourceProperties" va1ue="user=username;portNumber=8000"/>
	<property name="exclusiveConnectionMode" value="true"/>
</bean>

<bean id="datasource2" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
	<property name="uniqueResourceName" value="XADBMS_TWO"/>
	<property name="xaDataSourceClassName" value="COM.FirstSQL.Dbep.DbcpXADataSource"/>
	<property name="xaDataSourceProperties" value="user=username;portNumber=8000"/>
	<property name="exclusiveConnectionMode" value="true"/>
</bean>

<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
	<property name="forceShutdown" value="true"/>
</bean>

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
	<property name="transactionTimeout" value="200"/>
</bean>

<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
	<property name="transactionManager" ref="atomikosTransactionManager"/>
	<property name="userTransaction" ref="atomikosUserTransaction"/>
</bean>

<bean id="dao" class="...">
	<property name="dataSource" ref="datasource"/>
</bean>

我们需要告知Spring的JtaTransactionManager使用Atomikos的usertransaction和ttransactionManager实现。而至于Atomikos的userTransaction和transactionManager到底如何与RM(数据库、消息队列以及其他)进行交互,那就是Atomikos的事情了。

注意:不要被Atomikos的SimpleDataSourceBean的名字给迷惑了。另外,只要应用程序使用的数据库提供了有XA支持的数据库驱动,我们就可以通过SimpleDataSourceBean来配置需要的支持XA的DataSource,但各个参数的设置需要参考相应驱动程序的文档,比如xaDataSourceClassName以及xaDataSourceProperties属性。

多个Atomikos的SimpleDataSourceBean存在的情况下,它们对应的uniqueResourceName必须是不同的!

最后的内容是专门为Spring的JtaTransactionManager的,Spring2.5版本发布后,在XSD的配置中,可使用tx命名空间下专门为JtaTransactionManager提供的配置元素来简化其配置,如下所示:

<tx:jta-transaction-manager/>

21.4 小结

作为Spring事务抽象框架的扩展篇,本章主要挖掘了事务抽象框架中有关ThreadLocal的实践、策略模式的实践,以及分布式事务管理的简单分析这三点内容。我希望以这些内容为砖,能够引出更多的玉。

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/125499339