考虑这样的情况,你有一个类的实例,而且希望 同时插入事务性通知(advice)和一些简单的剖析(profiling)通知。那么,在<tx:annotation-driven/>环境中该怎么做?
我们调用 updateFoo(Foo) 方法时希望这样:
-
配置的剖析切面(profiling aspect)开始启动,
-
然后进入事务通知(根据配置创建一个新事务或加入一个已经存在的事务),
-
然后执行原始对象的方法,
-
然后事务提交(我们假定这里一切正常),
-
最后剖析切面报告整个事务方法执行过程花了多少时间。
![]() | Note |
|---|---|
| 这章不是专门讲述AOP的任何细节(除了应用于事务方面的之外)。请参考 Chapter 6, 使用Spring进行面向切面编程(AOP) 章以获得对各种AOP配置及其一般概念的详细叙述。 | |
这里有一份简单的剖析切面(profiling aspect)的代码。(注意,通知的顺序是由 Ordered 接口来控制的。要想了解更多细节,请参考 Section 6.2.4.7, “通知(Advice)顺序” 节。)
package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class SimpleProfiler implements Ordered {
private int order;
// allows us to control the ordering of advice
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
// this method is the around advice
public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}
这里是帮助满足我们上述要求的配置数据。
<?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">
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this is the aspect -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- execute before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<aop:config>
<!-- this advice will execute around the transactional advice -->
<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
上面配置的结果将获得到一个拥有剖析和事务方面的 按那样的顺序 应用于它上面的 'fooService' bean。 许多附加的方面的配置将一起达到这样的效果。
最后,下面的一些示例演示了使用纯XML声明的方法来达到上面一样的设置效果。
<?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">
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the profiling advice -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- execute before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>
<aop:config>
<aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>
<!-- will execute after the profiling advice (c.f. the order attribute) -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod"order="2"/>
<!-- order value is higher than the profiling aspect -->
<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</beans>
上面配置的结果是创建了一个 'fooService' bean,剖析方面和事务方面被 依照顺序 施加其上。如果我们希望剖析通知在目标方法执行之前 后于 事务通知执行,而且在目标方法执行之后先于 事务通知,我们可以简单地交换两个通知bean的order值。
如果配置中包含更多的方面,它们将以同样的方式受到影响。
通过AspectJ切面,你也可以在Spring容器之外使用Spring框架的 @Transactional 功能。要使用这项功能你必须先给相应的类和方法加上@Transactional注解,然后把 spring-aspects.jar 文件中定义的org.springframework.transaction.aspectj.AnnotationTransactionAspect 切面连接进(织入)你的应用。同样,该切面必须配置一个事务管理器。你当然可以通过Spring框架容器来处理注入,但因为我们这里关注于在Spring容器之外运行应用,我们将向你展示如何通过手动书写代码来完成。
![]() | Note |
|---|---|
| 在我们继续之前,你可能需要好好读一下前面的Section 9.5.6, “使用 @Transactional” 和Chapter 6, 使用Spring进行面向切面编程(AOP) 两章。 | |
// construct an appropriate transaction manager DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); // configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods AnnotationTransactionAspect.aspectOf().setTransactionManager (txManager);
![]() | Note |
|---|---|
| 使用此切面(aspect),你必须在 实现 类(和/或类里的方法)、而 不是 类的任何所实现的接口上面进行注解。AspectJ遵循Java的接口上的注解 不被继承 的规则。 | |
类上的 @Transactional 注解指定了类里的任何 public 方法执行的默认事务语义。
类里的方法的 @Transactional 将覆盖掉类注解的默认事务语义(如何存在的话)。 public、protected和默认可见的方法可能都被注解。直接对 protected和默认可见的方法进行注解,让这些方法在执行时去获取所定义的事务划分是唯一的途径。
要把 AnnotationTransactionAspect 织入你的应用,你或者基于AspectJ构建你的应用(参考AspectJ Development Guide),或者采取“载入时织入”(load-time weaving),参考 Section 6.8.4, “在Spring应用中使用AspectJ Load-time weaving(LTW)” 获得关于使用AspectJ进行“载入时织入”的讨论。
Spring提供两种方式的编程式事务管理:
-
使用 TransactionTemplate
-
直接使用一个 PlatformTransactionManager 实现
如果你选择编程式事务管理,Spring小组推荐你采用第一种方法(即使用 TransactionTemplate)。第二种方法类似使用JTA的UserTransaction API (除了异常处理简单点儿)。
TransactionTemplate 采用与Spring中别的 模板 同样的方法,如 JdbcTemplate 和 HibernateTemplate。它使用回调机制,将应用代码从样板式的资源获取和释放代码中解放出来,不再有大量的try/catch/finally/try/catch代码块。同样,和别的模板类一样,TransactionTemplate 类的实例是线程安全的。
必须在事务上下文中执行的应用代码看起来像这样:(注意使用 TransactionCallback 可以有返回值)
Object result = tt.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
如果不需要返回值,更方便的方式是创建一个 TransactionCallbackWithoutResult 的匿名类,如下:
tt.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
回调方法内的代码可以通过调用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。
想要使用 TransactionTemplate 的应用类必须能访问一个 PlatformTransactionManager(典型情况下通过依赖注入提供)。这样的类很容易做单元测试,只需要引入一个 PlatformTransactionManager 的伪类或桩类。这里没有JNDI查找、没有 静态 诡计,它是一个如此简单的接口。像往常一样,使用Spring给你的单元测试带来极大地简化。
你也可以直接使用 org.springframework.transaction.PlatformTransactionManager的实现来管理事务。只需通过bean引用简单地传入一个PlatformTransactionManager 实现,然后使用 TransactionDefinition 和 TransactionStatus 对象,你就可以启动一个事务,提交或回滚。
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// execute your business logic here
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
当你只有很少的事务操作时,编程式事务管理通常比较合适。例如,如果你有一个Web应用,其中只有特定的更新操作有事务要求,你可能不愿使用Spring或其他技术设置事务代理。这种情况下,使用TransactionTemplate 可能 是个好办法。
另一方面,如果你的应用中存在大量事务操作,那么声明式事务管理通常是值得的。它将事务管理与业务逻辑分离,而且在Spring中配置也不难。使用Spring,而不是EJB CMT,声明式事务管理在配置上的成本极大地降低了。
一般来说,Spring的事务抽象与应用服务器是无关的。另外,使用Spring的 JtaTransactionManager 类时,一种可选的方式是通过JNDI查询获得JTAUserTransaction 和 TransactionManager 对象,其中后者可以被设置为自动探测,这时针对不同的应用服务器有不同的方式。能够直接访问TransactionManager,确实在很大程度上增强了事务语义,可以参考 JtaTransactionManager 类的javadoc获得更多细节。
在一个使用WebLogic 7.0、8.1或更高版本的环境中,你一般会优先选用特定于WebLogic的 WebLogicJtaTransactionManager 类来取代基础的JtaTransactionManager 类。在WebLogic环境中,该类提供了对Spring事务定义的完全支持,超过了标准的JTA语义。它的特性包括:支持事务名,支持为每个事务定义隔离级别,以及在任何环境下正确地恢复事务的能力。
在WebSphere 5.1、5.0和4环境下,你可以使用Spring的 WebSphereTransactionManagerFactoryBean 类。这是一个工厂类,通过WebSphere的静态 访问方法获取到JTA TransactionManager 实例。(这些静态方法在每个版本的WebSphere中都不同。)一旦通过工厂bean获取到JTATransactionManager 实例,就可以使用该实例装配一个Spring的 JtaTransactionManager bean,它封装了JTA UserTransaction,提供增强的事务语义。请参考相关javadoc以获得完整信息。
开发者需要按照需求仔细地选择正确的 PlatformTransactionManager 实现。理解Spring的事务抽象如何与JTA全局事务一起工作是非常重要的。使用得当,就不会有任何冲突:Spring仅仅提供一个直观的、可移植的抽象层。
如果你使用全局事务,你 必须 为你的所有事务操作使用Spring的 org.springframework.transaction.jta.JtaTransactionManager 类(或 特定于某种应用服务器的子类)。否则Spring将试图在象容器数据源这样的资源上执行局部事务。这样的局部事务没有任何意义,好的应用服务器会把这些情况视为错误。
从下面的链接中你可以找到更多关于Spring框架事务支持方面的资源
-
来自 InfoQ 的 Java事务设计策略(Java Transaction Design Strategies) 是一本介绍Java事务方面相当好的书。同时它也包含了如何在Spring框架和EJB3中配置与使用事务的对比示例。
文章摘录自:http://www.redsaga.com/spring_ref/2.0/html/transaction.html
本文深入探讨了Spring框架中的事务管理,包括声明式和编程式事务管理的使用方法,以及如何结合AOP进行事务通知配置。此外,还介绍了如何在不同应用服务器环境中配置Spring事务。
![[Note]](http://www.redsaga.com/spring_ref/2.0/images/admons/note.png)
3562

被折叠的 条评论
为什么被折叠?



