




已阅读5页,还剩16页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念。Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用, 不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。从考虑EJB CMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。它们的基本方法是相似的: 都可以指定事务管理到单独的方法;如果需要可以在事务上下文调用 setRollbackOnly() 方法。不同之处在于: 不像EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。只需更改配置文件, 它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。 Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像EJB那样的特殊类。 Spring提供了声明式的 回滚规则:EJB没有对应的特性,我们将在下面讨论。回滚可以声明式的控制,不仅仅是编程式的。 Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。 你也可以增加任意的通知,就象事务通知一样。使用EJB CMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。 Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用EJB。 然而,不要轻易使用这些特性。因为通常我们并不希望事务跨越远程调用。TransactionProxyFactoryBean在哪儿?Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。主要差异在于不再需要配置TransactionProxyFactoryBean了。Spring2.0之前的旧版本风格的配置仍然是有效的;你可以简单地认为新的替你定义了TransactionProxyFactoryBean。回滚规则的概念比较重要:它使我们能够指定什么样的异常(和throwable)将导致自动回滚。我们在配置文件中声明式地指定,无须在Java代码中。同时,我们仍旧可以通过调用 TransactionStatus 的 setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则, 声明 MyApplicationException 必须总是导致事务回滚。 这种方式带来了显著的好处,它使你的业务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。对EJB来说,默认的行为是EJB容器在遇到 系统异常(通常指运行时异常)时自动回滚当前事务。 EJB CMT遇到 应用异常(例如,除了 java.rmi.RemoteException 外别的checked exception)时并不会自动回滚。 默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到unchecked exceptions时自动回滚),但通常定制这条规则会更有用。9.5.1.理解Spring的声明式事务管理实现本节的目的是消除与使用声明式事务管理有关的神秘性。简单点儿总是好的,这份参考文档只是告诉你给你的类加上Transactional注解,在配置文件中添加()行,然后期望你理解整个过程是怎么工作的。此节讲述Spring的声明式事务管理内部的工作机制,以帮助你在面对事务相关的问题时不至于误入迷途,回朔到上游平静的水域。在理解Spring的声明式事务管理方面最重要的概念是:Spring的事务管理是通过AOP代理实现的。 其中的事务通知由元数据(目前基于XML或注解)驱动。 代理对象与事务元数据结合产生了一个AOP代理,它使用一个PlatformTransactionManager 实现品配合TransactionInterceptor,在方法调用前后实施事务。注意尽管使用Spring声明式事务管理不需要AOP(尤其是Spring AOP)的知识,但了解这些是很有帮助的。你可以在 第6章 使用Spring进行面向切面编程(AOP) 章找到关于Spring AOP的全部内容。9.5.2.第一个例子请看下面的接口和它的实现。这个例子的意图是介绍概念,使用 Foo 和 Bar 这样的名字只是为了让你关注于事务的用法,而不是领域模型。/ 我们想做成事务性的服务接口package x.y.service;public interface FooService Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo);/ 上述接口的一个实现package x.y.service;public class DefaultFooService implements FooService public Foo getFoo(String fooName) throw new UnsupportedOperationException(); public Foo getFoo(String fooName, String barName) throw new UnsupportedOperationException(); public void insertFoo(Foo foo) throw new UnsupportedOperationException(); public void updateFoo(Foo foo) throw new UnsupportedOperationException(); (对该例的目的来说,上例中实现类(DefaultFooService)的每个方法在其方法体中抛出 UnsupportedOperationException 的做法是恰当的,我们可以看到,事务被创建出来, 响应 UnsupportedOperationException 的抛出,然后回滚。)我们假定,FooService的前两个方法(getFoo(String) 和getFoo(String, String))必须执行在只读事务上下文中,其他的方法(insertFoo(Foo)和 updateFoo(Foo))必须执行在可读写事务上下文中。不要想着一次理解下面的配置,所有内容都会在后面的章节详细讨论。 !- the transactional advice (what happens; see the bean below) - !- other definitions here -我们来分析一下上面的配置。我们要把一个服务对象(fooService bean)做成事务性的。 我们想施加的事务语义封装在定义中。 “把所有以 get 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。 其中的 transaction-manager 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 txManager), 该bean将会真正管理事务。提示事实上,如果 PlatformTransactionManager bean的名字是 transactionManager 的话,你的事务通知()中的 transaction-manager 属性可以忽略。否则你则需要像上例那样明确指定。配置中最后一段是 的定义, 它确保由 txAdvice bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 我们把该切面叫做 fooServiceOperation。然后我们用一个通知器(advisor)把这个切面与 txAdvice 绑定在一起, 表示当 fooServiceOperation 执行时,txAdvice 定义的通知逻辑将被执行。 元素定义是AspectJ的切面表示法,可参考Spring 2.0 第6章 使用Spring进行面向切面编程(AOP)章获得更详细的内容。一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如: (这个例子中假定你所有的服务接口定义在 x.y.service 包中。你同样可以参考 第6章 使用Spring进行面向切面编程(AOP) 章获得更详细内容。)现在,既然我们已经分析了整个配置,你可能会问了,“好吧,但是所有这些配置做了什么?”。上面的配置将为fooService bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。我们来看看下面的例子,测试一下上面的配置。public final class Boot public static void main(final String args) throws Exception ApplicationContext ctx = new ClassPathXmlApplicationContext(context.xml, Boot.class); FooService fooService = (FooService) ctx.getBean(fooService); fooService.insertFoo (new Foo(); 运行上面程序的输出结果看起来像这样(注意为了清楚起见,Log4J的消息和从 DefaultFooService 的 insertFoo(.) 方法抛出的 UnsupportedOperationException 异常堆栈信息被省略了)。AspectJInvocationContextExposingAdvisorAutoProxyCreator - Creating implicit proxy for bean fooService with 0 common interceptors and 1 specific interceptors JdkDynamicAopProxy - Creating JDK dynamic proxy for x.y.service.DefaultFooService TransactionInterceptor - Getting transaction for x.y.service.FooService.insertFoo DataSourceTransactionManager - Creating new transaction with name x.y.service.FooService.insertFooDataSourceTransactionManager - Acquired Connection mons.dbcp.PoolableConnectiona53de4 for JDBC transaction RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationExceptionTransactionInterceptor - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable java.lang.UnsupportedOperationException DataSourceTransactionManager - Rolling back JDBC transaction on Connection mons.dbcp.PoolableConnectiona53de4DataSourceTransactionManager - Releasing JDBC Connection after transactionDataSourceUtils - Returning JDBC Connection to DataSourceException in thread main java.lang.UnsupportedOperationExceptionat x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14) at $Proxy0.insertFoo(Unknown Source)at Boot.main(Boot.java:11)9.5.3.回滚在前面的章节里,概述了如何在你的应用中用声明的风格为类(特别是服务层的类)指定事务配置。 这一章将描述如何使用一个简单的声明式配置来控制事务的回滚。我们推荐做法是在Spring框架的事务架构里指出当context的事务里的代码抛出 Exception 时事务进行回滚。Spring框架的事务基础架构代码将从调用的堆栈里捕获到任何未处理的 Exception,并将标识事务将回滚。然而,请注意Spring框架的事务基础架构代码将默认地 只 在抛出运行时和unchecked exceptions时才标识事务回滚。 也就是说,当抛出一个 RuntimeException 或其子类例的实例时。(Errors 也一样 - 默认地 - 标识事务回滚。)从事务方法中抛出的Checked exceptions将 不 被标识进行事务回滚。可以配置哪些 Exception类型将被标识进行事务回滚。 下面的XML配置片断里示范了如何配置一个用于回滚的checked、应用程序特定的 Exception 类型。 有时候你不想在异常抛出的时候回滚事务,就可以使用“不回滚规则”。 在下面的例子中,我们告诉Spring 框架即使遇到没有经过处理的InstrumentNotFoundException异常,也不要回滚事务。 当Spring框架捕获到一个异常后会检查配置回滚规则来决定是不是要回滚事务,这时候会遵循最匹配的规则。 所以在下面这种配置中,除了InstrumentNotFoundException这种类型的异常不会导致事务回滚以外,其他任何类型的异常都会。 第二种方法是通过 编程式 方式来指定回滚事务。 虽然写法非常的简单,但是这个方法是高侵入性的,并且使你的代码与Spring框架的事务架构高度耦合。 下面的代码片断里示范了Spring框架管理事务的编程式回滚:public void resolvePosition() try / some business logic. catch (NoProductInStockException ex) / trigger rollback programmatically TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 强烈推荐你尽可能地使用声明式事务回滚方法。 编程式方法的回滚对你来说是可见,如果你需要它你就可以使用,但是使用它就直接违反了在你的应用中使用一个纯基于POJO的模型。9.5.4.为不同的bean配置不同的事务语义现在让我们考虑一下这样的场景,假设你有许多服务对象,你想为他们分别设置 完全不同 的事务语义。 在Spring中,你可以通过分别定义特定的 元素, 让每个advisor采用不同的 pointcut 和 advice-ref 属性,来达到目的。让我们假定你所有的服务层类定义在以 x.y.service 为根的包内。 为了让service包(或子包)下所有名字以 Service 结尾的类的对象拥有默认的事务语义,你可以做如下的配置: 下面的配置示例演示了两个拥有完全不同的事务配置的bean。 9.5.5. 有关的设置这一节里将描述通过 标签来指定不同的事务性设置。默认的 设置如下: 事务传播设置 是 REQUIRED 隔离级别是DEFAULT 事务是 读/写 事务超时默认是依赖于事务系统的,或者事务超时没有被支持。 任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚这些默认的设置当然也是可以被改变的。 和 标签里的 各种属性设置总结如下:表9.1. 有关的设置属性是否需要?默认值描述name是与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:get*、handle*、on*Event等等。propagation不REQUIRED事务传播行为isolation不DEFAULT事务隔离级别timeout不-1事务超时的时间(以秒为单位)read-only不false事务是否只读?rollback-for不将被触发进行回滚的 Exception(s);以逗号分开。 如:com.foo.MyBusinessException,ServletExceptionno-rollback-for不不 被触发进行回滚的 Exception(s);以逗号分开。 如:com.foo.MyBusinessException,ServletException在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是包含完整包名的类名加上.和方法名,比如 com.foo.BusinessService.handlePayment.9.5.6.使用 Transactional注意Transactional 注解及其支持类所提供的功能最低要求使用Java 5(Tiger)。除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。下面的例子很好地演示了 Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义:/ the service class that we want to make transactionalTransactionalpublic class DefaultFooService implements FooService Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo);当上述的POJO定义在Spring IoC容器里时,上述bean实例仅仅通过一 行xml配置就可以使它具有事务性的。如下: !- other definitions here -提示实际上,如果你用 transactionManager 来定义 PlatformTransactionManager bean的名字的话,你就可以忽略 标签里的 transaction-manager 属性。 如果 PlatformTransactionManager bean你要通过其它名称来注入的话,你必须用 transaction-manager 属性来指定它,如上所示。方法的可见度和 Transactional在使用代理的时候,Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 Transactional 注解,系统也不会报错, 但是这个被注解的方法将不会执行已配置的事务设置。如果你非要注解非公共方法的话,请参考使用AspectJ (参见下面)。Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。 然而,请注意只是使用 Transactional 注解并不会启用事务行为, 它仅仅 是一种元数据,能够被可以识别 Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。Spring团队的建议是你只在具体的类上使用 Transactional 注解, 而不要注解在接口上。你当然可以在接口(或接口方法)上使用 Transactional 注解, 但是这只有在你使用基于接口的代理时它才会生效。因为注解是 不能继承 的, 这就意味着如果你正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,而且对象也不会被事务代理所包装 (这是很糟糕的)。 因此,请接受Spring团队的建议,在具体的类(包括该类的方法)上使用 Transactional 注解。注意:在代理模式下(默认的情况),只有从代理传过来的外部方法调用才会被拦截。 这就意味着自我调用是不会触发事务的,比如说,一个在目标对象中调用目标对象其他方法的方法是不会触发一个事务的,即使这个方法被标记为 Transactional!如果你期望自我调用被事务覆盖到,可以考虑使用AspectJ 模式(如下所示)。在这种情况下,一开始就没有任何代理的存在; 为了把Transactional的方法变成运行时的行为,目标类会被编织起来(比如修改它的字节码)。表9.2. 设置属性默认值描述transaction-managertransactionManager使用的事务管理器的名字。只有像在上面的例子那样,事务管理器不是 transactionManager的情况下才需要。modeproxy默认的模式“proxy”会用Spring的AOP框架来代理注解过的bean(就像在前面讨论过的那样, 下面代理的语义只对通过代理传递过来的方法调用起效)。 另一种可行的模式aspectj会使用Spring的AspectJ事务切面来编织类(通过修改目标对象的字节码应用到任何方法调用上)。 AspectJ织入需要在classpath中有spring-aspects.jar这个文件,并且启用装载时织入 (或者编译时织入). (关于如何设置装载时编织的详情请参见 第节 “Spring配置” )注意在元素上的proxy-target-class 属性 控制了有什么类型的事务性代理会为使用Transactional 来注解的类创建代理。 如果proxy-target-class 属性被设为true,那么基于类的代理就会被创建。 如果proxy-target-class 属性被设为false 或者没设,那么会创建基于接口的标准JDK代理。(关于不同代理类型的解释请参见 第6.6节 “代理机制”)在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: DefaultFooService 类在类的级别上被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 Transactional 注解的事务设置将优先于类级别注解的事务设置。Transactional(readOnly = true)public class DefaultFooService implements FooService public Foo getFoo(String fooName) / do something / these settings have precedence for this method Transactional(readOnly = false, pro
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025房地产预告抵押合同(绿色生态农业示范区)
- 疫情宣传课件
- 投资合作协议说明和细则
- 2026届山东省聊城市重点达标名校中考冲刺卷英语试题含答案
- 海底资源开发投资协议
- 疫情健康主题班会课件
- 疫情上网课的课件
- 智慧城市交通管理系统开发合作合同
- 全新供气合作合同
- 广西贺州初一数学试卷
- 陕西2020-2024年中考英语五年真题汇编教师版-专题04 阅读理解之说明文
- 航天航空科普知识单选题100道及答案解析
- 电影《白日梦想家》课件
- 北京市昌平区2023-2024学年八年级上学期期末语文试题(解析版)
- 实验活动2 水的组成及变化的探究说课稿-2024-2025学年九年级化学人教版(2024)上册
- 打更劳务合同范本(2篇)
- GB/T 15843.2-2024网络安全技术实体鉴别第2部分:采用鉴别式加密的机制
- 七年级上学期有理数训练题
- 陪诊服务协议
- 产品代理合同协议(2024版)
- 高考英语核心高频688词
评论
0/150
提交评论