Advising Transactional Operations
假设你想同时运行事务性操作和一些基本的剖析 advice。你如何在 <tx:annotation-driven/> 的上下文中实现这一点?
当你调用 updateFoo(Foo)方法时,你想看到以下动作:
- 配置的剖析切面开始。
- 事务性 advice 运行。
- 被 advice 对象上的方法运行。
- 事务提交。
- 剖析切面报告整个事务性方法调用的确切时间。
本章不涉及解释 AOP 的任何细节(除了它适用于事务)。关于 AOP 配置和一般 AOP 的详细内容,请参见 AOP。
下面的代码显示了前面讨论的简单剖析切面:
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;// 允许我们控制 advice 的顺序public int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}// 这个方法是一个环绕 advicepublic 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;}}
advice 的排序是通过 Ordered 接口控制的。关于接口排序的全部细节,请参阅 advice 排序。
下面的配置创建了一个 fooService 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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="fooService" class="x.y.service.DefaultFooService"/><!-- 这个是一个切面(aspect) --><bean id="profiler" class="x.y.SimpleProfiler"><!-- 在事物 之前运行,因此 order 比 事物的 advice 小 --><property name="order" value="1"/></bean><!-- 定义了事务性 advice 的顺序为 200 --><tx:annotation-driven transaction-manager="txManager" order="200"/><aop:config><!-- 这个 advice 是围绕事务 advice 进行的 --><aop:aspect id="profilingAspect" ref="profiler">!-- 这个切入点针对 所有以 Service 结尾内的所有方法 --><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>
你可以用类似的方式配置任何数量的附加切面。
下面的例子创建了与前两个例子相同的设置,但使用了纯粹的 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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="fooService" class="x.y.service.DefaultFooService"/><!-- the profiling advice --><bean id="profiler" class="x.y.SimpleProfiler"><!-- run 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.*(..))"/><!-- runs after the profiling advice (cf. 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 TransactionManager here --></beans>
前面配置的结果是一个 fooService bean,它按照这个顺序应用了剖析和事务性 advice。如果你想让剖析 advice 在交事物 advice 之后运行,在事物 advice 之前运行,你可以交换剖析切面 Bean 的顺序属性的值,使其高于事务 advice 的顺序值。
你可以用类似的方式配置其他切面。
