Advising Transactional Operations

    假设你想同时运行事务性操作和一些基本的剖析 advice。你如何在 <tx:annotation-driven/> 的上下文中实现这一点?

    当你调用 updateFoo(Foo)方法时,你想看到以下动作:

    • 配置的剖析切面开始。
    • 事务性 advice 运行。
    • 被 advice 对象上的方法运行。
    • 事务提交。
    • 剖析切面报告整个事务性方法调用的确切时间。

    本章不涉及解释 AOP 的任何细节(除了它适用于事务)。关于 AOP 配置和一般 AOP 的详细内容,请参见 AOP

    下面的代码显示了前面讨论的简单剖析切面:

    1. package x.y;
    2. import org.aspectj.lang.ProceedingJoinPoint;
    3. import org.springframework.util.StopWatch;
    4. import org.springframework.core.Ordered;
    5. public class SimpleProfiler implements Ordered {
    6. private int order;
    7. // 允许我们控制 advice 的顺序
    8. public int getOrder() {
    9. return this.order;
    10. }
    11. public void setOrder(int order) {
    12. this.order = order;
    13. }
    14. // 这个方法是一个环绕 advice
    15. public Object profile(ProceedingJoinPoint call) throws Throwable {
    16. Object returnValue;
    17. StopWatch clock = new StopWatch(getClass().getName());
    18. try {
    19. clock.start(call.toShortString());
    20. returnValue = call.proceed();
    21. } finally {
    22. clock.stop();
    23. System.out.println(clock.prettyPrint());
    24. }
    25. return returnValue;
    26. }
    27. }

    advice 的排序是通过 Ordered 接口控制的。关于接口排序的全部细节,请参阅 advice 排序

    下面的配置创建了一个 fooService Bean,它以所需的顺序将剖析和事务性切面应用于它:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:aop="http://www.springframework.org/schema/aop"
    5. xmlns:tx="http://www.springframework.org/schema/tx"
    6. xsi:schemaLocation="
    7. http://www.springframework.org/schema/beans
    8. https://www.springframework.org/schema/beans/spring-beans.xsd
    9. http://www.springframework.org/schema/tx
    10. https://www.springframework.org/schema/tx/spring-tx.xsd
    11. http://www.springframework.org/schema/aop
    12. https://www.springframework.org/schema/aop/spring-aop.xsd">
    13. <bean id="fooService" class="x.y.service.DefaultFooService"/>
    14. <!-- 这个是一个切面(aspect) -->
    15. <bean id="profiler" class="x.y.SimpleProfiler">
    16. <!-- 在事物 之前运行,因此 order 比 事物的 advice 小 -->
    17. <property name="order" value="1"/>
    18. </bean>
    19. <!-- 定义了事务性 advice 的顺序为 200 -->
    20. <tx:annotation-driven transaction-manager="txManager" order="200"/>
    21. <aop:config>
    22. <!-- 这个 advice 是围绕事务 advice 进行的 -->
    23. <aop:aspect id="profilingAspect" ref="profiler">
    24. !-- 这个切入点针对 所有以 Service 结尾内的所有方法 -->
    25. <aop:pointcut id="serviceMethodWithReturnValue"
    26. expression="execution(!void x.y..*Service.*(..))"/>
    27. <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
    28. </aop:aspect>
    29. </aop:config>
    30. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    31. <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    32. <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
    33. <property name="username" value="scott"/>
    34. <property name="password" value="tiger"/>
    35. </bean>
    36. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    37. <property name="dataSource" ref="dataSource"/>
    38. </bean>
    39. </beans>

    你可以用类似的方式配置任何数量的附加切面。

    下面的例子创建了与前两个例子相同的设置,但使用了纯粹的 XML 声明式方法:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:aop="http://www.springframework.org/schema/aop"
    5. xmlns:tx="http://www.springframework.org/schema/tx"
    6. xsi:schemaLocation="
    7. http://www.springframework.org/schema/beans
    8. https://www.springframework.org/schema/beans/spring-beans.xsd
    9. http://www.springframework.org/schema/tx
    10. https://www.springframework.org/schema/tx/spring-tx.xsd
    11. http://www.springframework.org/schema/aop
    12. https://www.springframework.org/schema/aop/spring-aop.xsd">
    13. <bean id="fooService" class="x.y.service.DefaultFooService"/>
    14. <!-- the profiling advice -->
    15. <bean id="profiler" class="x.y.SimpleProfiler">
    16. <!-- run before the transactional advice (hence the lower order number) -->
    17. <property name="order" value="1"/>
    18. </bean>
    19. <aop:config>
    20. <aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>
    21. <!-- runs after the profiling advice (cf. the order attribute) -->
    22. <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" order="2"/>
    23. <!-- order value is higher than the profiling aspect -->
    24. <aop:aspect id="profilingAspect" ref="profiler">
    25. <aop:pointcut id="serviceMethodWithReturnValue"
    26. expression="execution(!void x.y..*Service.*(..))"/>
    27. <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
    28. </aop:aspect>
    29. </aop:config>
    30. <tx:advice id="txAdvice" transaction-manager="txManager">
    31. <tx:attributes>
    32. <tx:method name="get*" read-only="true"/>
    33. <tx:method name="*"/>
    34. </tx:attributes>
    35. </tx:advice>
    36. <!-- other <bean/> definitions such as a DataSource and a TransactionManager here -->
    37. </beans>

    前面配置的结果是一个 fooService bean,它按照这个顺序应用了剖析和事务性 advice。如果你想让剖析 advice 在交事物 advice 之后运行,在事物 advice 之前运行,你可以交换剖析切面 Bean 的顺序属性的值,使其高于事务 advice 的顺序值。

    你可以用类似的方式配置其他切面。