您的位置:首页 > 编程语言 > Java开发

谈 Spring-Transaction(Spring事务管理 第三篇)

2012-08-15 11:42 453 查看
我们来分析一下上面的配置。我们要把一个服务对象('fooService' bean)做成事务性的。我们想施加的事务语义封装在<tx:advice/>定义中。<tx:advice/> “把所有以
'get' 开头的方法看做执行在只读事务上下文中,其余的方法执行在默认语义的事务上下文中”。 其中的
'transaction-manager' 属性被设置为一个指向
PlatformTransactionManager bean的名字(这里指 'txManager'),该bean将实际上实施事务管理。


Tip
事实上,如果 PlatformTransactionManager bean的名字是
'transactionManager' 的话,你的事务通知(<tx:advice/>)中的
'transaction-manager' 属性可以忽略。否则你则需要像上例那样明确指定。

配置中最后一段是 <aop:config/> 的定义,它确保由 'txAdvice' bean定义的事务通知在应用中合适的点被执行。首先我们定义了 一个切面,它匹配
FooService 接口定义的所有操作,我们把该切面叫做
'fooServiceOperation'。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起,表示当
'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。

<aop:pointcut/> 元素定义是AspectJ的切面表示法,可参考Spring 2.0
Chapter 6, 使用Spring进行面向切面编程(AOP)章获得更详细的内容。

一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面表达式匹配服务层的所有操作方法。例如:

<aop:config>
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>

(这个例子中假定你所有的服务接口定义在 'x.y.service' 包中。你同样可以参考

Chapter 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 异常堆栈信息被省略了)。

<!-- Spring容器开始启动... -->

[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy
for bean 'fooService' with 0 common interceptors and 1 specific interceptors
<!-- the DefaultFooService is actually proxied -->

[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]

<!-- ... the insertFoo(..) method is now being invoked on the proxy -->

[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
<!-- the transactional advice kicks in here... -->

[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection
[org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction

<!-- the insertFoo(..) method from DefaultFooService throws an exception... -->

[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should
rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo
due to throwable [java.lang.UnsupportedOperationException]

<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->

[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection
[org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource

Exception in thread "main" java.lang.UnsupportedOperationException
at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<!-- AOP infrastructure stack trace elements removed for clarity -->

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 类型来标识事务回滚。

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="false" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

第二方法是在Spring框架的事务架构里通过 编程式 方式指出一个事务将被回滚。 虽然这个非常简单,但是这个方法对于Spring框架的事务架构来说,在你的代码是高入侵的和紧藕合的 下面的代码片断里示范了Spring框架管理事务的编程式回滚:

public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}

强烈推荐你尽可能地使用声明式事务回滚方法。 编程式方法的回滚对你来说是可见,如果你需要它你就可以使用,但是使用它就直接违反了在你的应用中使用一个纯基于POJO的模型。

9.5.4. 为不同的bean配置不同的事务语义

现在我们考虑一下这样的场景,你有许多服务对象,而且想为不同组的对象设置 完全不同 的事务语义。在Spring中,你可以通过定义各自特定的
<aop:advisor/> 元素,每个advisor采用不同的 'pointcut' 和
'advice-ref' 属性,来达到目的。

借助于一个例子,我们假定你所有的服务层类定义在以 'x.y.service' 为根的包内。为了让service包(或子包)下所有名字以
'Service' 结尾的类的对象拥有默认的事务语义,你可以配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://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/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<aop:config>

<aop:pointcut id="serviceOperation" expression="execution(* x.y.service..*Service.*(..))"/>

<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

</aop:config>

<!-- these two beans will be transactional... -->

<bean id="fooService" class="x.y.service.DefaultFooService"/>
<bean id="barService" class="x.y.service.extras.SimpleBarService"/>

<!-- ...and these two beans won't -->

<bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
<bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') -->

<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

下面的配置示例演示了两个不同的bean拥有完全不同的事务配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://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/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<aop:config>

<aop:pointcut id="defaultServiceOperation" expression="execution(* x.y.service.*Service.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

<aop:pointcut id="noTxServiceOperation" expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

</aop:config>

<!-- this bean will be transactional (c.f. the 'defaultServiceOperation' pointcut) -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>

<!-- this bean will also be transactional, but with totally different transactional settings -->
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

<tx:advice id="defaultTxAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>

<!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: