通知的事务操作

假设您想运行事务操作和一些基本的性能分析通知。如何在 <tx:annotation-driven/> 的上下文中实现这一点?

当您调用 updateFoo(Foo) 方法时,您希望看到以下操作:

  • 配置的性能分析切面启动。

  • 事务通知运行。

  • 被通知对象上的方法运行。

  • 事务提交。

  • 性能分析切面报告整个事务方法调用的确切持续时间。

本章不打算非常详细地解释 AOP(除了它适用于事务)。有关 AOP 配置和一般 AOP 的详细介绍,请参阅 AOP

以下代码显示了前面讨论的简单性能分析切面:

  • Java

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import infra.util.StopWatch;
import infra.core.Ordered;

public class SimpleProfiler implements Ordered {

  private int order;

  // 允许我们要控制通知的顺序
  public int getOrder() {
    return this.order;
  }

  public void setOrder(int order) {
    this.order = order;
  }

  // 此方法是环绕通知
  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;
  }
}

通知的顺序通过 Ordered 接口控制。有关通知顺序的完整详细信息,请参阅 通知顺序

以下配置创建了一个 fooService bean,该 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"/>

  <!-- 这是切面 -->
  <bean id="profiler" class="x.y.SimpleProfiler">
    <!-- 在事务通知之前运行(因此顺序号较低) -->
    <property name="order" value="1"/>
  </bean>

  <tx:annotation-driven transaction-manager="txManager" order="200"/>

  <aop:config>
      <!-- 此通知在事务通知周围运行 -->
      <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="infra.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"/>

  <!-- 性能分析通知 -->
  <bean id="profiler" class="x.y.SimpleProfiler">
    <!-- 在事务通知之前运行(因此顺序号较低) -->
    <property name="order" value="1"/>
  </bean>

  <aop:config>
    <aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>
    <!-- 在性能分析通知之后运行(参见 order 属性) -->

    <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" order="2"/>
    <!-- 顺序值高于性能分析切面 -->

    <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>

  <!-- 其他 <bean/> 定义,如 DataSource 和 TransactionManager 在这里 -->

</beans>

上述配置的结果是一个 fooService bean,它按该顺序应用了性能分析和事务切面。如果您希望性能分析通知在进入时在事务通知之后运行,在退出时在事务通知之前运行,您可以交换性能分析切面 bean 的 order 属性的值,使其高于事务通知的顺序值。

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