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;
}
// 这个方法是一个环绕 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;
}
}
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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://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 的顺序值。
你可以用类似的方式配置其他切面。