Spring25声明式事务管理详解.docx
- 文档编号:3639625
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:23
- 大小:31.08KB
Spring25声明式事务管理详解.docx
《Spring25声明式事务管理详解.docx》由会员分享,可在线阅读,更多相关《Spring25声明式事务管理详解.docx(23页珍藏版)》请在冰豆网上搜索。
Spring25声明式事务管理详解
大多数Spring用户选择声明式事务管理。
这是对应用代码影响最小的选择,因此也最符合非侵入式轻量级容器的理念。
Spring的声明式事务管理是通过SpringAOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用,不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。
从考虑EJBCMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。
它们的基本方法是相似的:
都可以指定事务管理到单独的方法;如果需要可以在事务上下文调用setRollbackOnly()方法。
不同之处在于:
∙不像EJBCMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。
只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。
∙Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像EJB那样的特殊类。
∙Spring提供了声明式的回滚规则:
EJB没有对应的特性,我们将在下面讨论。
回滚可以声明式的控制,不仅仅是编程式的。
∙Spring允许你通过AOP定制事务行为。
例如,如果需要,你可以在事务回滚中插入定制的行为。
你也可以增加任意的通知,就象事务通知一样。
使用EJBCMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。
∙Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。
如果你需要这些特性,我们推荐你使用EJB。
然而,不要轻易使用这些特性。
因为通常我们并不希望事务跨越远程调用。
TransactionProxyFactoryBean在哪儿?
Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。
主要差异在于不再需要配置TransactionProxyFactoryBean了。
Spring2.0之前的旧版本风格的配置仍然是有效的;你可以简单地认为新的 tags/>替你定义了TransactionProxyFactoryBean。 回滚规则的概念比较重要: 它使我们能够指定什么样的异常(和throwable)将导致自动回滚。 我们在配置文件中声明式地指定,无须在Java代码中。 同时,我们仍旧可以通过调用TransactionStatus的setRollbackOnly()方法编程式地回滚当前事务。 通常,我们定义一条规则,声明MyApplicationException必须总是导致事务回滚。 这种方式带来了显著的好处,它使你的业务对象不必依赖于事务设施。 典型的例子是你不必在代码中导入SpringAPI,事务等。 对EJB来说,默认的行为是EJB容器在遇到系统异常(通常指运行时异常)时自动回滚当前事务。 EJBCMT遇到应用异常(例如,除了java.rmi.RemoteException外别的checkedexception)时并不会自动回滚。 默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到uncheckedexceptions时自动回滚),但通常定制这条规则会更有用。 9.5.1. 理解Spring的声明式事务管理实现 本节的目的是消除与使用声明式事务管理有关的神秘性。 简单点儿总是好的,这份参考文档只是告诉你给你的类加上@Transactional注解,在配置文件中添加(' annotation-driven/>')行,然后期望你理解整个过程是怎么工作的。 此节讲述Spring的声明式事务管理内部的工作机制,以帮助你在面对事务相关的问题时不至于误入迷途,回朔到上游平静的水域。 在理解Spring的声明式事务管理方面最重要的概念是: Spring的事务管理是通过AOP代理实现的。 其中的事务通知由元数据(目前基于XML或注解)驱动。 代理对象与事务元数据结合产生了一个AOP代理,它使用一个PlatformTransactionManager实现品配合TransactionInterceptor,在方法调用前后实施事务。 注意 尽管使用Spring声明式事务管理不需要AOP(尤其是SpringAOP)的知识,但了解这些是很有帮助的。 你可以在第 6 章使用Spring进行面向切面编程(AOP)章找到关于SpringAOP的全部内容。 9.5.2. 第一个例子 请看下面的接口和它的实现。 这个例子的意图是介绍概念,使用Foo和Bar这样的名字只是为了让你关注于事务的用法,而不是领域模型。 //我们想做成事务性的服务接口 packagex.y.service; publicinterfaceFooService{ FoogetFoo(StringfooName); FoogetFoo(StringfooName,StringbarName); voidinsertFoo(Foofoo); voidupdateFoo(Foofoo); } //上述接口的一个实现 packagex.y.service; publicclassDefaultFooServiceimplementsFooService{ publicFoogetFoo(StringfooName){ thrownewUnsupportedOperationException(); } publicFoogetFoo(StringfooName,StringbarName){ thrownewUnsupportedOperationException(); } publicvoidinsertFoo(Foofoo){ thrownewUnsupportedOperationException(); } publicvoidupdateFoo(Foofoo){ thrownewUnsupportedOperationException(); } } (对该例的目的来说,上例中实现类(DefaultFooService)的每个方法在其方法体中抛出UnsupportedOperationException的做法是恰当的,我们可以看到,事务被创建出来,响应UnsupportedOperationException的抛出,然后回滚。 ) 我们假定,FooService的前两个方法(getFoo(String)和getFoo(String,String))必须执行在只读事务上下文中,其他的方法(insertFoo(Foo)和updateFoo(Foo))必须执行在可读写事务上下文中。 不要想着一次理解下面的配置,所有内容都会在后面的章节详细讨论。 --fromthefile'context.xml'--> xmlversion="1.0"encoding="UTF-8"? > //www.springframework.org/schema/beans" xmlns: xsi="http: //www.w3.org/2001/XMLSchema-instance" xmlns: aop="http: //www.springframework.org/schema/aop" xmlns: tx="http: //www.springframework.org/schema/tx" xsi: schemaLocation=" http: //www.springframework.org/schema/beanshttp: //www.springframework.org/schema/beans/spring-beans-2.5.xsd http: //www.springframework.org/schema/txhttp: //www.springframework.org/schema/tx/spring-tx-2.5.xsd http: //www.springframework.org/schema/aophttp: //www.springframework.org/schema/aop/spring-aop-2.5.xsd"> --thisistheserviceobjectthatwewanttomaketransactional--> --thetransactionaladvice(what'happens';seethe advisor/>beanbelow)--> adviceid="txAdvice"transaction-manager="txManager"> --thetransactionalsemantics...--> attributes> --allmethodsstartingwith'get'areread-only--> methodname="get*"read-only="true"/> --othermethodsusethedefaulttransactionsettings(seebelow)--> methodname="*"/> attributes> advice> --ensurethattheabovetransactionaladvicerunsforanyexecution ofanoperationdefinedbytheFooServiceinterface--> config> pointcutid="fooServiceOperation"expression="execution(*x.y.service.FooService.*(..))"/> advisoradvice-ref="txAdvice"pointcut-ref="fooServiceOperation"/> config> --don'tforgettheDataSource--> oracle: thin: @rj-t42: 1521: elvis"/> --similarly,don'tforgetthePlatformTransactionManager--> --other 我们来分析一下上面的配置。 我们要把一个服务对象('fooService'bean)做成事务性的。 我们想施加的事务语义封装在 advice/>定义中。 advice/>“把所有以'get'开头的方法看做执行在只读事务上下文中,其余的方法执行在默认语义的事务上下文中”。 其中的'transaction-manager'属性被设置为一个指向PlatformTransactionManagerbean的名字(这里指'txManager'),该bean将会真正管理事务。 提示 事实上,如果PlatformTransactionManagerbean的名字是'transactionManager'的话,你的事务通知( advice/>)中的'transaction-manager'属性可以忽略。 否则你则需要像上例那样明确指定。 配置中最后一段是 config/>的定义,它确保由'txAdvice'bean定义的事务通知在应用中合适的点被执行。 首先我们定义了一个切面,它匹配FooService接口定义的所有操作,我们把该切面叫做'fooServiceOperation'。 然后我们用一个通知器(advisor)把这个切面与'txAdvice'绑定在一起,表示当'fooServiceOperation'执行时,'txAdvice'定义的通知逻辑将被执行。 pointcut/>元素定义是AspectJ的切面表示法,可参考Spring2.0第 6 章使用Spring进行面向切面编程(AOP)章获得更详细的内容。 一个普遍性的需求是让整个服务层成为事务性的。 满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。 例如: config> pointcutid="fooServiceMethods"expression="execution(*x.y.service.*.*(..))"/> advisoradvice-ref="txAdvice"pointcut-ref="fooServiceMethods"/> config> (这个例子中假定你所有的服务接口定义在'x.y.service'包中。 你同样可以参考第 6 章使用Spring进行面向切面编程(AOP)章获得更详细内容。 ) 现在,既然我们已经分析了整个配置,你可能会问了,“好吧,但是所有这些配置做了什么? ”。 上面的配置将为'fooService'bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。 我们来看看下面的例子,测试一下上面的配置。 publicfinalclassBoot{ publicstaticvoidmain(finalString[]args)throwsException{ ApplicationContextctx=newClassPathXmlApplicationContext("context.xml",Boot.class); FooServicefooService=(FooService)ctx.getBean("fooService"); fooService.insertFoo(newFoo()); } } 运行上面程序的输出结果看起来像这样(注意为了清楚起见,Log4J的消息和从DefaultFooService的insertFoo(..)方法抛出的UnsupportedOperationException异常堆栈信息被省略了)。 --Spring容器开始启动...--> [AspectJInvocationContextExposingAdvisorAutoProxyCreator]-Creatingimplicitproxy forbean'fooService'with0commoninterceptorsand1specificinterceptors --theDefaultFooServiceisactuallyproxied--> [JdkDynamicAopProxy]-CreatingJDKdynamicproxyfor[x.y.service.DefaultFooService] --...theinsertFoo(..)methodisnowbeinginvokedontheproxy--> [TransactionInterceptor]-Gettingtransactionforx.y.service.FooService.insertFoo --thetransactionaladvicekicksinhere...--> [DataSourceTransactionManager]-Creatingnewtransactionwithname[x.y.service.FooService.insertFoo] [DataSourceTransactionManager]-AcquiredConnection [mons.dbcp.PoolableConnection@a53de4]forJDBCtransaction --theinsertFoo(..)methodfromDefaultFooServicethrowsanexception...--> [RuleBasedTransactionAttribute]-Applyingrulestodeterminewhethertransactionshould rollbackonjava.lang.UnsupportedOperationException [TransactionInterceptor]-Invokingrollbackfortransactiononx.y.service.FooService.insertFoo duetothrowable[java.lang.UnsupportedOperationException] --andthetransactionisrolledback(bydefault,RuntimeExceptioninstancescauserollback)--> [DataSourceTransactionManager]-RollingbackJDBCtransactiononConnection [mons.dbcp.PoolableConnection@a53de4] [DataSourceTransactionManager]-ReleasingJDBCConnectionaftertransaction [DataSourceUtils]-ReturningJDBCConnectiontoDataSource Exceptioninthread"main"java.lang.UnsupportedOperationException atx.y.service.DefaultFooService.insertFoo(DefaultFooService.java: 14) --AOPinfrastructurestacktraceelementsremovedforclarity--> at$Proxy0.insertFoo(UnknownSource) atBoot.main(Boot.java: 11) 9.5.3. 回滚 在前面的章节里,概述了如何在你的应用中用声明的风格为类(特别是服务层的类)指定事务配置。 这一章将描述如何使用一个简单的声明式配置来控制事务的回滚。 我们推荐做法是在Spring框架的事务架构里指出当context的事务里的代码抛出Exception时事务进行回滚。 Spring框架的事务基础架构代码将从调用的堆栈里捕获到任何未处理的Exception,并将标识事务将回滚。 然而,请注意Spring框架的事务基础架构代码将默认地只在抛出运行时和uncheckedexceptions时才标识事务回滚。 也就是说,当抛出一个RuntimeException或其子类例的实例时。 (Errors也一样-默认地-标识事务回滚。 )从事务方法中抛出的Checkedexceptions将不被标识进行事务回滚。 可以配置哪些Exception类型将被标识进行事务回滚。 下面的XML配置片断里示范了如何配置一个用于回滚的checked、应用程序特定的Exception类型。 adviceid="txAdvice"transaction-manager="txManager"> attributes> methodname="get*"read-only="true"rollback-for="NoProductInStockException"/> methodname="*"/> attributes> advice> 有时候你不想在异常抛出的时候回滚事务,就可以使用“不回滚规则”。 在下面的例子中,我们告诉Spring框架即使遇到没有经过处理的InstrumentNotFoundException异常,也不要回滚事务。 adviceid="txAdvice"> attributes> methodname="updateStock"no-rollback-for="InstrumentNotFoundException"/> methodname="*"/> attributes> advice> 当Spring框架捕获到一个异常后会检查配置回滚规则来决定是不是要回滚事务,这时候会遵循最匹配的规则。 所以在下面这种配置中,除了InstrumentNotFoundException这种类型的异常不会导致事务回滚以外,其他任何类型的异常都会。 adviceid="txAdvice"> attributes> methodname="*"rollback-for="Throwable"no-rollback-for="InstrumentNotFoundException"/> attributes> advice> 第二种方法是通过编程式方式来指定回滚事务。 虽然写法非常的简单,但是这个方法是高侵入性的,并且使你的代码与Spring框架的事务架构高度耦合。 下面的代码片断里示范了Spring框架管理事务的编程式回滚: publicvoidresolvePosition(){ try{ //somebusinesslogic... }catch(NoProductInStockExceptionex){ //triggerrollbackprogrammatically TransactionAspect
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Spring25 声明 事务管理 详解