トランザクション操作のアドバイス

トランザクション操作といくつかの基本的なプロファイリングアドバイスの両方を実行するとします。<tx:annotation-driven/> のコンテキストでこれをどのように実行しますか?

updateFoo(Foo) メソッドを呼び出すと、次のアクションが表示されます。

  • 設定されたプロファイリングアスペクトが開始されます。

  • トランザクションアドバイスが実行されます。

  • アドバイスされたオブジェクトのメソッドが実行されます。

  • トランザクションがコミットします。

  • プロファイリングアスペクトは、トランザクションメソッド呼び出し全体の正確な期間を報告します。

この章は、AOP の詳細な説明には関係ありません(トランザクションに適用される場合を除く)。AOP 設定と AOP 全般の詳細については、AOP を参照してください。

次のコードは、前述の単純なプロファイリングのアスペクトを示しています。

  • Java

  • Kotlin

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;
	}
}
package x.y

import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.util.StopWatch
import org.springframework.core.Ordered

class SimpleProfiler : Ordered {

	private var order: Int = 0

	// allows us to control the ordering of advice
	override fun getOrder(): Int {
		return this.order
	}

	fun setOrder(order: Int) {
		this.order = order
	}

	// this method is the around advice
	fun profile(call: ProceedingJoinPoint): Any {
		var returnValue: Any
		val clock = StopWatch(javaClass.name)
		try {
			clock.start(call.toShortString())
			returnValue = call.proceed()
		} finally {
			clock.stop()
			println(clock.prettyPrint())
		}
		return returnValue
	}
}

アドバイスの順序は、Ordered インターフェースを介して制御されます。アドバイスのオーダーの詳細については、アドバイスのオーダーを参照してください。

次の構成では、プロファイリングおよびトランザクションのアスペクトが目的の順序で適用される 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"/>

	<!-- this is the aspect -->
	<bean id="profiler" class="x.y.SimpleProfiler">
		<!-- run before the transactional advice (hence the lower order number) -->
		<property name="order" value="1"/>
	</bean>

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

	<aop:config>
			<!-- this advice runs 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>

同様の方法で、任意の数の追加のアスペクトを構成できます。

次の例では、前の 2 つの例と同じセットアップを作成しますが、純粋な 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 が作成され、プロファイリングとトランザクションのアスペクトがこの順序で適用されます。プロファイリングアドバイスを、トランザクションアドバイスの後、トランザクションアドバイスの前に実行する場合は、プロファイリングアスペクト Bean の order プロパティの値を入れ替えて、トランザクションアドバイスのオーダー値よりも高くすることができます。

同様の方法で追加のアスペクトを構成できます。