读书人

Spring中的事宜

发布时间: 2012-08-26 16:48:05 作者: rapoo

Spring中的事务

6.3? Spring的事务
Spring的事务管理不需与任何特定的事务API耦合。对不同的持久层访问技术,编程式事务提供一致
的事务编程风格,通过模板化的操作一致性地管理事务。声明式事务基于Spring AOP实现,却并不需
要程序开发者成为AOP专家,亦可轻易使用Spring的声明式事务管理。

6.3.1? Spring支持的事务策略

Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。
该接口的源代码如下:

??
对于容器管理JTA数据源,全局事务策略的配置文件如下:

?对于采用Hibernate持久层访问策略时,局部事务策略的配置文件如下:

?对于采用Hibernate持久层访问策略时,全局事务策略的配置文件如下:

?不论采用哪种持久层访问技术,只要使用JTA数据源,Spring事务管理器的配置都是一样的,因为它
们都采用的是全局事务管理。
可以看到,仅仅通过配置文件的修改,就可以在不同的事务管理策略间切换,即使从局部事务到全局
事务的切换。
提示:Spring所支持的事务策略非常灵活,Spring的事务策略允许应用程序在不同事务策略之间自由
切换,即使需要在局部事务策略和全局事务策略之间切换,只需要修改配置文件,而应用程序的代码
无须任何改变。这种灵活的设计,又何尝不是因为面向接口编程带来的优势,可见面向接口编程给应
用程序更好的适应性。

6.3.2? Spring事务策略的优势

虽然在上面的配置片段中,仅仅配置了JDBC局部事务管理器、Hibernate局部事务管理器、JDBC全局
事务管理器等。但Spring支持大部分持久化策略的事务管理器。
不论采用何种持久化策略,Spring都提供了一致的事务抽象,因此,应用开发者能在任何环境下,使
用一致的编程模型。无须更改代码,应用就可在不同的事务管理策略中切换。Spring同时支持声明式
事务管理和编程式事务管理。
使用编程式事务管理,开发者使用的是Spring事务抽象,而无须使用任何具体的底层事务API。
Spring的事务管理将代码从底层具体的事务API中抽象出来,该抽象可以使用在任何底层事务基础之
上。
使用声明式策略,开发者通常书写很少的事务管理代码,因此,不依赖Spring或任何其他事务API。
Spring的声明式事务无须任何额外的容器支持,Spring容器本身管理声明式事务。使用声明事务策略
,无须在业务代码中书写任何事务代码,可以让开发者更好地专注于业务逻辑的实现。Spring管理的
事务支持多个事务资源的跨越,但无法支持跨越远程调用的事务上下文传播。

6.3.3? 使用TransactionProxyFactoryBean创建事务代理

Spring同时支持编程式事务策略和声明式事务策略,大部分时候,都推荐采用声明式事务策略,使用
声明事务策略的优势十分明显:
? ● 声明式事务能大大降低开发者的代码书写量。而且声明式事务几乎不需要影响应用的代码。因
此,无论底层事务策略如何变化,应用程序无须任何改变。
? ● 应用程序代码无须任何事务处理代码,可以更专注于业务逻辑的实现。
? ● Spring则可对任何POJO的方法提供事务管理,而且Spring的声明式事务管理无须容器的支持,
可在任何环境下使用。
? ● EJB的CMT无法提供声明式回滚规则。而通过配置文件,Spring可指定事务在遇到特定异常时自
动回滚。Spring不仅可在代码中使用setRollbackOnly回滚事务,也可在配置文件中配置回滚规则。
? ● 由于Spring采用AOP的方式管理事务,因此,可以在事务回滚动作中插入用户自己的动作,而不
仅仅是执行系统默认的回滚。
提示:本节不打算全面介绍Spring的各种事务策略,因此本节不会介绍编程式事务。如果读者需要更
全面了解Spring事务的相关方面,请参阅笔者所著的《Spring2.0宝典》???? 一书。
对于采用声明式事务策略,可以使用TransactionProxyFactoryBean来配置事务代理Bean。正如它的
类名所暗示的,它是一个工厂Bean,工厂Bean用于生成一系列的Bean实例,这一系列的Bean实例都是
Proxy。
可能读者已经想到了,既然TransactionProxyFactoryBean产生的是代理Bean,可见这种事务代理正
是基于Spring AOP组件的。配置TransactionProxyFactoryBean时,一样需要指定目标Bean。
每个TransactionProxyFactoryBean为一个目标Bean生成事务代理,事务代理的方法改写了目标Bean
的方法,就是在目标Bean的方法执行之前加入开始事务,在目标Bean的方法正常结束之前提交事务,
如果遇到特定异常则回滚事务。
TransactionProxyFactoryBean创建事务代理时,需要了解当前事务所处的环境,该环境属性通过
PlatformTransactionManager实例传入,而相关事务传入规则在TransactionProxy- FactoryBean的
定义中给出。
下面给出声明式事务配置文件的完整代码:

?在上面的定义文件中,没有对DAO对象采用Service层包装。通常情况下,DAO层上应有一层Service层
。事务代理则以Service层Bean为目标Bean。此处为了简化配置,TransactionProxyFactoryBean直接
以DAO bean作为目标bean,这一点不会影响事务代理的生成。
事务回滚规则部分定义了三个回滚规则:
第一个回滚规则表示所有以insert开始的方法,都应该满足该事务规则。PROPAGATION_REQUIRED事务
传播规则指明,该方法必须处于事务环境中,如果当前执行线程已处于事务环境下,则直接执行;否
则,启动新的事务然后执行该方法。该规则还指定,如果方法抛出MyCheckedException的实例及其子
类的实例,则强制回滚。MyCheckedException前的“-”表示强制回滚;“+”则表示强制提交,即某
些情况下,即使抛出异常也强制提交;
第二个回滚规则表示所有以update开头的方法,都遵守PROPAGATION_REQUIRED的事务传播规则;
第三个回滚规则表示除前面规定的方法外,其他所有方法都采用PROPAGATION_ REQUIRED事务传播规
则,而且只读。
常见的事务传播规则有如下几个:
? ● PROPAGATION_MANDATORY,要求调用该方法的线程必须处于事务环境中,否则抛出异常。
? ● PROPAGATION_NESTED,如果执行该方法的线程已处于事务环境下,依然启动新的事务,方法在
嵌套的事务里执行。如果执行该方法的线程序并未处于事务中,也启动新的事务,然后执行该方法,
此时与PROPAGATION_REQUIRED相同。
? ● PROPAGATION_NEVER,不允许调用该方法的线程处于事务环境下,如果调用该方法的线程处于事
务环境下,则抛出异常。
? ● PROPAGATION_NOT_SUPPORTED,如果调用该方法的线程处在事务中,则先暂停当前事务,然后执
行该方法。
? ● PROPAGATION_REQUIRED,要求在事务环境中执行该方法,如果当前执行线程已处于事务中,则
直接调用;如果当前执行线程不处于事务中,则启动新的事务后执行该方法。
? ● PROPAGATION_REQUIRES_NEW,该方法要求有一个线程在新的事务环境中执行,如果当前执行线
程已处于事务中,先暂停当前事务,启动新的事务后执行该方法;如果当前调用线程不处于事务中,
则启动新的事务后执行该方法。
? ● PROPAGATION_SUPPORTS,如果当前执行线程处于事务中,则使用当前事务,否则不使用事务。
程序里原来使用personDAO的地方,无须变化。因为,配置文件里将personDAO目标Bean的id改成
personDAOTarget,为TransactionProxyFactoryBean工厂Bean所产生的代理Bean命名为personDAO。
该代理Bean会包含原有personDAO的所有方法,而且为这些方法增加了不同的事务处理规则。
程序面向PersonDaoImpl类所实现的接口编程,TransactionProxyFactoryBean生成的代理Bean也会实
现TransactionProxyFactoryBean接口。因此,原有的程序中调用DAO组件的代码无须任何改变。程序
运行时,由事务代理完成原来目标Bean完成的工作。
事实上,Spring不仅支持对接口的代理,整合CGLIB后,Spring甚至可对具体类生成代理。只要设置
proxyTargetClass属性为true就可以。如果目标Bean没有实现任何接口,proxyTargetClass属性默认
被设为true,此时Spring会对具体类生成代理。当然,通常建议面向接口编程,而不要面向具体的实
现类编程。

6.3.4? 使用继承简化事务配置

仔细观察配置文件中两个事务代理Bean的配置时,发现两个事务代理Bean的大部分配置完全相同,如
果配置文件中包含大量这样的事务代理Bean配置,配置文件将非常臃肿。考虑到大部分事务代理Bean
的配置都大同小异,可以使用Bean继承来简化事务代理的配置。
正如前面部分介绍的,Bean继承是将所有子Bean中相同的配置定义成一个模板,并将此模板Bean定义
成一个抽象Bean。考虑所有事务代理Bean中,有如下部分是大致相?? 同的:
? ● 事务代理Bean所使用的事务管理器。
? ● 事务传播规则。
因此,现在配置文件中定义如下的事务代理模板Bean,其配置代码如下:

?而真正的事务代理Bean,则改为继承上面的事务模板Bean。考虑到将目标Bean定义在Spring容器中可
能增加未知的风险,因此将目标Bean定义成嵌套Bean。

?可见,采用Bean继承方式定义事务代理的方式,可以很好地简化事务代理的配置,可以避免配置事务
代理Bean时的冗余配置。
提示:使用Bean继承可以很好地简化事务代理Bean的配置,通过将各事务代理Bean共同的配置信息提
取成事务模板Bean,可以让实际的事务代理Bean的配置更加简洁;而且,配置方式相当直观。尽量将
目标Bean配置成嵌套Bean,这样的方式可以保证更好的内聚性。
如果读者还记得前面介绍的AOP知识,应该知道还有一种更加简洁的配置,就是利用Bean后处理器,
让Bean后处理器为容器中其他Bean自动创建事务代理。

6.3.5? 使用自动创建代理简化事务配置

回顾6.2.6节和6.2.7节,读者可能已经想到如何自动创建代理。是的,正是通过6.2.6节和6.2.7节所
给出的两个自动代理创建类来生成事务代理。
正如前文已经提到的,使用BeanNameAutoProxyCreator和DefaultAdvisorAutoProxy- Creator来创建
代理时,并不一定是创建事务代理,关键在于传入的拦截器,如果传入事务拦截器,将可自动生成事
务代理。
下面是使用BeanNameAutoProxyCreator自动生成事务代理的配置文件:

?如果配置文件中仅有两个目标Bean,可能不能很清楚地看出这种自动创建代理配置方式的优势,但如果有更多目标Bean需要自动创建事务代理,则可以很好地体会到这种配置方式的优势:配置文件只需
要简单地配置目标Bean,然后在BeanNameAutoProxyCreator配置中增加一行即可。
提示:使用BeanNameAutoProxyCreator可以自动创建事务代理,使用DefaultAdvisor-
AutoProxyCreator也可自动创建事务代理。关于后一个Bean后处理器的配置方式,请参看前面6.2.7节的内容。

读书人网 >软件架构设计

热点推荐