アドバイスを宣言する

アドバイスはポイントカット式に関連付けられており、ポイントカットに一致するメソッド実行の前、後、または前後で実行されます。ポイントカット式は、インラインポイントカットまたは名前付きポイントカットへの参照のいずれかです。

Before アドバイス

@Before アノテーションを使用して、アスペクトでアドバイスの前に宣言できます。

次の例では、インラインポイントカット式を使用しています。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

	@Before("execution(* com.xyz.dao.*.*(..))")
	public void doAccessCheck() {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before

@Aspect
class BeforeExample {

	@Before("execution(* com.xyz.dao.*.*(..))")
	fun doAccessCheck() {
		// ...
	}
}

名前付きポイントカットを使用する場合、前の例を次のように書き換えることができます。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

	@Before("com.xyz.CommonPointcuts.dataAccessOperation()")
	public void doAccessCheck() {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before

@Aspect
class BeforeExample {

	@Before("com.xyz.CommonPointcuts.dataAccessOperation()")
	fun doAccessCheck() {
		// ...
	}
}

After Returning アドバイス

After returning アドバイスは、一致したメソッドの実行が正常に戻ったときに実行されます。@AfterReturning アノテーションを使用して宣言できます。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

	@AfterReturning("execution(* com.xyz.dao.*.*(..))")
	public void doAccessCheck() {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning

@Aspect
class AfterReturningExample {

	@AfterReturning("execution(* com.xyz.dao.*.*(..))")
	fun doAccessCheck() {
		// ...
	}
}
複数のアドバイス宣言(およびその他のメンバー)を、すべて同じアスペクト内に含めることができます。これらの例では、それぞれの効果に焦点を当てるために、1 つのアドバイス宣言のみを示しています。

場合によっては、返された実際の値にアドバイス本文でアクセスする必要があります。次の例に示すように、戻り値をバインドする @AfterReturning の形式を使用して、そのアクセスを取得できます。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

	@AfterReturning(
		pointcut="execution(* com.xyz.dao.*.*(..))",
		returning="retVal")
	public void doAccessCheck(Object retVal) {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning

@Aspect
class AfterReturningExample {

	@AfterReturning(
		pointcut = "execution(* com.xyz.dao.*.*(..))",
		returning = "retVal")
	fun doAccessCheck(retVal: Any?) {
		// ...
	}
}

returning 属性で使用される名前は、advice メソッドのパラメーターの名前に対応している必要があります。メソッドの実行が戻ると、戻り値は対応する引数値としてアドバイスメソッドに渡されます。returning 句は、指定された型の値を返すメソッドの実行のみに一致を制限します(この場合、戻り値に一致する Object)。

after returning アドバイスを使用する場合、まったく異なる参照を返すことはできないことに注意してください。

After Throwing アドバイス

After throwing アドバイスは、一致したメソッドの実行が例外をスローして終了したときに実行されます。次の例に示すように、@AfterThrowing アノテーションを使用して宣言できます。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

	@AfterThrowing("execution(* com.xyz.dao.*.*(..))")
	public void doRecoveryActions() {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing

@Aspect
class AfterThrowingExample {

	@AfterThrowing("execution(* com.xyz.dao.*.*(..))")
	fun doRecoveryActions() {
		// ...
	}
}

多くの場合、特定の型の例外がスローされたときにのみアドバイスを実行したい場合があります。また、アドバイス本体のスローされた例外へのアクセスも必要になることがよくあります。throwing 属性を使用して、一致を制限し(必要に応じて - そうでない場合は Throwable を例外型として使用)、スローされた例外をアドバイスパラメーターにバインドできます。次の例は、その方法を示しています。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

	@AfterThrowing(
		pointcut="execution(* com.xyz.dao.*.*(..))",
		throwing="ex")
	public void doRecoveryActions(DataAccessException ex) {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing

@Aspect
class AfterThrowingExample {

	@AfterThrowing(
		pointcut = "execution(* com.xyz.dao.*.*(..))",
		throwing = "ex")
	fun doRecoveryActions(ex: DataAccessException) {
		// ...
	}
}

throwing 属性で使用される名前は、advice メソッドのパラメーターの名前に対応している必要があります。メソッドの実行が例外をスローして終了すると、例外は対応する引数値としてアドバイスメソッドに渡されます。throwing 句は、指定された型(この場合は DataAccessException)の例外をスローするメソッドの実行のみに一致を制限します。

@AfterThrowing は、一般的な例外処理コールバックを示していないことに注意してください。具体的には、@AfterThrowing アドバイスメソッドは、ジョインポイント(ユーザーが宣言したターゲットメソッド)自体からのみ例外を受け取ることになっていますが、付随する @After/@AfterReturning メソッドからは受け取りません。

After (Finally) アドバイス

After (finally) アドバイスは、一致したメソッドの実行が終了すると実行されます。@After アノテーションを使用して宣言されます。After アドバイスは、通常の戻り条件と例外の戻り条件の両方を処理するように準備する必要があります。通常、リソースの解放などに使用されます。次の例は、after finally アドバイスの使用方法を示しています。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

	@After("execution(* com.xyz.dao.*.*(..))")
	public void doReleaseLock() {
		// ...
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.After

@Aspect
class AfterFinallyExample {

	@After("execution(* com.xyz.dao.*.*(..))")
	fun doReleaseLock() {
		// ...
	}
}

AspectJ の @After アドバイスは、try-catch ステートメントの finally ブロックに類似した「after finally アドバイス」として定義されていることに注意してください。これは、成功した通常のリターンにのみ適用される @AfterReturning とは対照的に、ジョインポイント(ユーザーが宣言したターゲットメソッド)からスローされた結果、通常のリターン、例外に対して呼び出されます。

Around アドバイス

最後の種類のアドバイスアドバイスに関するものです。Around アドバイスは、一致したメソッドの実行の「周り」で実行されます。メソッドの実行前と実行後の両方で作業を行い、いつ、どのように、メソッドが実際に実行されるかどうかを判断する機会があります。Around アドバイスは、メソッドの実行の前後でスレッドセーフな方法で状態を共有する必要がある場合によく使用されます。たとえば、タイマーの開始と停止などです。

要件を満たす最も強力でない形式のアドバイスを常に使用してください。

例: アドバイスがあなたのニーズ十分である場合は、アドバイスの周囲を使用しないでください。

Around アドバイスは、メソッドに @Around アノテーションを付けることによって宣言されます。メソッドはその戻り型として Object を宣言する必要があり、メソッドの最初のパラメーターは ProceedingJoinPoint 型である必要があります。アドバイスメソッドの本体内で、基になるメソッドを実行するために、ProceedingJoinPoint で proceed() を呼び出す必要があります。引数なしで proceed() を呼び出すと、呼び出し元の元の引数が、呼び出されたときに基になるメソッドに提供されます。高度なユースケースでは、引数の配列(Object[])を受け入れる proceed() メソッドのオーバーロードされたバリアントがあります。配列内の値は、呼び出されたときに基になるメソッドへの引数として使用されます。

Object[] で呼び出されたときの proceed の動作は、AspectJ コンパイラーによってコンパイルされたアドバイスに関する proceed の動作とは少し異なります。従来の AspectJ 言語を使用して記述されたアラウンドアドバイスの場合、proceed に渡される引数の数は、アラウンドアドバイスに渡される引数の数(基になるジョインポイントによって取得される引数の数ではない)と一致する必要があります。与えられた引数の位置は、値がバインドされたエンティティのジョインポイントの元の値に取って代わります(これが今のところ意味をなさなくても心配不要です)。

Spring が採用したアプローチはより単純であり、プロキシベースの実行のみのセマンティクスによりよく一致します。Spring 用に記述された @AspectJ アスペクトをコンパイルし、AspectJ コンパイラーとウィーバーで引数付きの proceed を使用する場合にのみ、この違いに注意する必要があります。Spring AOP と AspectJ の両方で 100% 互換性のあるアスペクトを作成する方法があります。これについては、アドバイスパラメーターに関する次のセクションで説明します。

アラウンドアドバイスによって返される値は、メソッドの呼び出し元から見た戻り値です。例: 単純なキャッシングアスペクトは、キャッシュがある場合はキャッシュから値を返すか、ない場合は proceed() を呼び出す(そしてその値を返す)ことができます。proceed は、周囲のアドバイスの本文内で 1 回、何度も呼び出されるか、まったく呼び出されない可能性があることに注意してください。これらはすべて正当です。

アラウンドアドバイスメソッドの return 型を void として宣言すると、null は常に呼び出し元に返され、proceed() の呼び出しの結果は事実上無視されます。アラウンドアドバイスメソッドは Object の戻り値の型を宣言することをお勧めします。基礎となるメソッドが void の戻り型を持っている場合でも、advice メソッドは通常、proceed() の呼び出しから返された値を返す必要があります。ただし、アドバイスは、ユースケースに応じて、オプションでキャッシュされた値、ラップされた値、その他の値を返す場合があります。

次の例は、アラウンドアドバイスの使用方法を示しています。

  • Java

  • Kotlin

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

	@Around("execution(* com.xyz..service.*.*(..))")
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		// start stopwatch
		Object retVal = pjp.proceed();
		// stop stopwatch
		return retVal;
	}
}
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.ProceedingJoinPoint

@Aspect
class AroundExample {

	@Around("execution(* com.xyz..service.*.*(..))")
	fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
		// start stopwatch
		val retVal = pjp.proceed()
		// stop stopwatch
		return retVal
	}
}

アドバイスパラメーター

Spring は完全に型付けされたアドバイスを提供します。つまり、Object[] 配列を常に使用するのではなく、アドバイス署名で必要なパラメーターを宣言することを意味します(返り値とスローの例で前述)このセクションの後半で、アドバイス本体で引数やその他のコンテキスト値を使用できるようにする方法を確認します。最初に、アドバイスが現在アドバイスしている方法を知ることができる一般的なアドバイスを書く方法を見てみましょう。

現在の JoinPoint へのアクセス

どのアドバイスメソッドも、最初のパラメーターとして、型 org.aspectj.lang.JoinPoint のパラメーターを宣言できます。JoinPoint のサブクラスである型 ProceedingJoinPoint の最初のパラメーターを宣言するには、周りのアドバイスが必要であることに注意してください。

JoinPoint インターフェースは、いくつかの便利な方法を提供します。

  • getArgs(): メソッドの引数を返します。

  • getThis(): プロキシオブジェクトを返します。

  • getTarget(): ターゲットオブジェクトを返します。

  • getSignature(): アドバイスされているメソッドの説明を返します。

  • toString(): 推奨されている方法の有用な説明を出力します。

詳細については、javadoc (英語) を参照してください。

アドバイスにパラメーターを渡す

戻り値または例外値をバインドする方法についてはすでに説明しました(戻り値と after throwing アドバイスの後に使用)。引数値をアドバイス本文で使用できるようにするには、args のバインディング形式を使用できます。args 式で型名の代わりにパラメーター名を使用すると、アドバイスが呼び出されたときに、対応する引数の値がパラメーター値として渡されます。例はこれをより明確にする必要があります。Account オブジェクトを最初のパラメーターとして受け取る DAO 操作の実行をアドバイスしたいとし、アドバイス本文のアカウントにアクセスする必要があるとします。次のように書くことができます:

  • Java

  • Kotlin

@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) {
	// ...
}
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
fun validateAccount(account: Account) {
	// ...
}

ポイントカット式の args(account,..) 部分には 2 つの目的があります。まず、メソッドが少なくとも 1 つのパラメーターを取り、そのパラメーターに渡される引数が Account のインスタンスであるメソッド実行のみに一致を制限します。次に、account パラメーターを介して、実際の Account オブジェクトをアドバイスで利用できるようにします。

これを記述する別の方法は、Account オブジェクト値がジョインポイントと一致するときにそれを「提供する」ポイントカットを宣言し、アドバイスから名前付きポイントカットを参照することです。これは次のようになります。

  • Java

  • Kotlin

@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
	// ...
}
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private fun accountDataAccessOperation(account: Account) {
}

@Before("accountDataAccessOperation(account)")
fun validateAccount(account: Account) {
	// ...
}

詳細については、AspectJ プログラミングガイドを参照してください。

プロキシオブジェクト (this)、ターゲットオブジェクト (target)、およびアノテーション (@within@target@annotation@args) はすべて、同様の方法でバインドできます。次の一連の例は、@Auditable アノテーションが付けられたメソッドの実行を照合し、監査コードを抽出する方法を示しています。

以下は、@Auditable アノテーションの定義を示しています。

  • Java

  • Kotlin

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
	AuditCode value();
}
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Auditable(val value: AuditCode)

以下は、@Auditable メソッドの実行に一致するアドバイスを示しています。

  • Java

  • Kotlin

@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") (1)
public void audit(Auditable auditable) {
	AuditCode code = auditable.value();
	// ...
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") (1)
fun audit(auditable: Auditable) {
	val code = auditable.value()
	// ...
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。

アドバイスパラメーターとジェネリクス

Spring AOP は、クラス宣言およびメソッドパラメーターで使用されるジェネリクスを処理できます。次のようなジェネリクス型があるとします。

  • Java

  • Kotlin

public interface Sample<T> {
	void sampleGenericMethod(T param);
	void sampleGenericCollectionMethod(Collection<T> param);
}
interface Sample<T> {
	fun sampleGenericMethod(param: T)
	fun sampleGenericCollectionMethod(param: Collection<T>)
}

メソッドをインターセプトするパラメーター型にアドバイスパラメーターを関連付けることにより、メソッド型のインターセプトを特定のパラメーター型に制限できます。

  • Java

  • Kotlin

@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
	// Advice implementation
}
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
fun beforeSampleMethod(param: MyType) {
	// Advice implementation
}

このアプローチは、ジェネリクスコレクションでは機能しません。次のようにポイントカットを定義することはできません。

  • Java

  • Kotlin

@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
	// Advice implementation
}
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
fun beforeSampleMethod(param: Collection<MyType>) {
	// Advice implementation
}

これを機能させるには、コレクションのすべての要素をインスペクションする必要がありますが、これは合理的ではありません。null 値の一般的な扱い方も決定できないためです。これに似た何かを実現するには、Collection<?> にパラメーターを入力し、要素の型を手動で確認する必要があります。

引数名の決定

アドバイス呼び出しでのパラメーターのバインドは、ポイントカット式で使用される名前と、アドバイスおよびポイントカットメソッドシグネチャーで宣言されたパラメーター名との一致に依存します。

AspectJ API はパラメーター名を引数名として参照するため、このセクションでは引数パラメーターという用語を同じ意味で使用します。

Spring AOP は、次の ParameterNameDiscoverer 実装を使用してパラメーター名を決定します。各発見者にはパラメーター名を発見する機会が与えられ、最初に成功した発見者が勝ちます。登録されたディスカバリのいずれもパラメーター名を判別できない場合は、例外がスローされます。

AspectJAnnotationParameterNameDiscoverer

対応するアドバイスまたはポイントカットアノテーションの argNames 属性を介してユーザーが明示的に指定したパラメーター名を使用します。詳細については、明示的な引数名を参照してください。

KotlinReflectionParameterNameDiscoverer

Kotlin リフレクション API を使用してパラメーター名を決定します。このディスカバーは、そのような API がクラスパスに存在する場合にのみ使用されます。

StandardReflectionParameterNameDiscoverer

標準の java.lang.reflect.Parameter API を使用してパラメーター名を決定します。javac の -parameters フラグを使用してコードをコンパイルする必要があります。Java 8+ で推奨されるアプローチ。

AspectJAdviceParameterNameDiscoverer

ポイントカット式、returningthrowing 句からパラメーター名を推測します。使用されるアルゴリズムの詳細については、javadoc を参照してください。

明示的な引数名

@AspectJ アドバイスとポイントカットアノテーションには、オプションの argNames 属性があり、アノテーション付きメソッドの引数名を指定するために使用できます。

デバッグ情報がなくても @AspectJ アスペクトが AspectJ コンパイラー (ajc) によってコンパイルされている場合は、必要な情報がコンパイラーによって保持されるため、argNames 属性を追加する必要はありません。

同様に、@AspectJ アスペクトが -parameters フラグを使用して javac でコンパイルされた場合、コンパイラーが必要な情報を保持するため、argNames 属性を追加する必要はありません。

次の例は、argNames 属性の使用方法を示しています。

  • Java

  • Kotlin

@Before(
	value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", (1)
	argNames = "bean,auditable") (2)
public void audit(Object bean, Auditable auditable) {
	AuditCode code = auditable.value();
	// ... use code and bean
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
2bean と auditable を引数名として宣言します。
@Before(
	value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", (1)
	argNames = "bean,auditable") (2)
fun audit(bean: Any, auditable: Auditable) {
	val code = auditable.value()
	// ... use code and bean
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
2bean と auditable を引数名として宣言します。

最初のパラメーターの型が JoinPointProceedingJoinPoint、または JoinPoint.StaticPart の場合、argNames 属性の値からパラメーターの名前を省略できます。例: 前述のアドバイスを変更してジョインポイントオブジェクトを受け取る場合、argNames 属性にそれを含める必要はありません。

  • Java

  • Kotlin

@Before(
	value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", (1)
	argNames = "bean,auditable") (2)
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
	AuditCode code = auditable.value();
	// ... use code, bean, and jp
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
2bean と auditable を引数名として宣言します。
@Before(
	value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", (1)
	argNames = "bean,auditable") (2)
fun audit(jp: JoinPoint, bean: Any, auditable: Auditable) {
	val code = auditable.value()
	// ... use code, bean, and jp
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
2bean と auditable を引数名として宣言します。

型 JoinPointProceedingJoinPoint、または JoinPoint.StaticPart の最初のパラメーターに与えられる特別な処理は、他のジョインポイントコンテキストを収集しないアドバイスメソッドに特に便利です。このような状況では、argNames 属性を省略できます。例: 次のアドバイスでは、argNames 属性を宣言する必要はありません。

  • Java

  • Kotlin

@Before("com.xyz.Pointcuts.publicMethod()") (1)
public void audit(JoinPoint jp) {
	// ... use jp
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。
@Before("com.xyz.Pointcuts.publicMethod()") (1)
fun audit(jp: JoinPoint) {
	// ... use jp
}
1 ポイントカット式の組み合わせで定義された publicMethod という名前のポイントカットを参照します。

引数付きで続行

Spring AOP と AspectJ で一貫して動作する引数を使用して proceed 呼び出しを記述する方法を説明することを以前に述べました。解決策は、アドバイス署名が各メソッドパラメーターを順番にバインドするようにすることです。次の例は、その方法を示しています。

  • Java

  • Kotlin

@Around("execution(List<Account> find*(..)) && " +
		"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
		"args(accountHolderNamePattern)") (1)
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
		String accountHolderNamePattern) throws Throwable {
	String newPattern = preProcess(accountHolderNamePattern);
	return pjp.proceed(new Object[] {newPattern});
}
1 名前付きポイントカット定義の共有で定義された inDataAccessLayer という名前のポイントカットを参照します。
@Around("execution(List<Account> find*(..)) && " +
		"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
		"args(accountHolderNamePattern)") (1)
fun preProcessQueryPattern(pjp: ProceedingJoinPoint,
						accountHolderNamePattern: String): Any? {
	val newPattern = preProcess(accountHolderNamePattern)
	return pjp.proceed(arrayOf<Any>(newPattern))
}
1 名前付きポイントカット定義の共有で定義された inDataAccessLayer という名前のポイントカットを参照します。

多くの場合、このバインディングを行います(前の例のように)。

アドバイスのオーダー

複数のアドバイスがすべて同じジョインポイントで実行されるとどうなるでしょうか? Spring AOP は、AspectJ と同じ優先順位ルールに従って、アドバイスの実行順序を決定します。最も優先順位の高いアドバイスが最初に「途中」で実行されます(つまり、2 つの before アドバイスが与えられた場合、最も優先順位の高いアドバイスが最初に実行されます)。ジョインポイントから「途中」では、最も優先順位の高いアドバイスが最後に実行されます(したがって、2 つの after アドバイスが与えられた場合、最も優先順位の高いものが 2 番目に実行されます)。

異なるアスペクトで定義された 2 つのアドバイスが両方とも同じジョインポイントで実行する必要がある場合、特に指定しない限り、実行順序は定義されていません。優先順位を指定することにより、実行の順序を制御できます。これは、アスペクトクラスで org.springframework.core.Ordered インターフェースを実装するか、@Order アノテーションを付けて通常の Spring の方法で行われます。2 つのアスペクトを考えると、Ordered.getOrder() から低い値(またはアノテーション値)を返すアスペクトの優先順位が高くなります。

特定のアスペクトの個別のアドバイス型はそれぞれ、概念的にはジョインポイントに直接適用することを目的としています。結果として、@AfterThrowing アドバイスメソッドは、付随する @After/@AfterReturning メソッドから例外を受け取ることは想定されていません。

Spring Framework 5.2.7 の時点で、同じ @Aspect クラスで定義され、同じジョインポイントで実行する必要があるアドバイスメソッドには、アドバイス型に基づいて、優先順位の高いものから低いものの順に優先順位が割り当てられます: @Around@Before@After@AfterReturning@AfterThrowing。ただし、@After アドバイスメソッドは、同じアスペクトの @AfterReturning または @AfterThrowing アドバイスメソッドの後に、AspectJ の @After の「after finally アドバイス」セマンティクスに従って効果的に呼び出されることに注意してください。

同じ @Aspect クラスで定義された同じ型のアドバイス(たとえば、2 つの @After アドバイスメソッド)の 2 つの部分が同じジョインポイントで実行される必要がある場合、順序は定義されていません(ソースコード宣言を取得する方法がないため) javac でコンパイルされたクラスのリフレクションを介してオーダーします)。このようなアドバイスメソッドを、各 @Aspect クラスのジョインポイントごとに 1 つのアドバイスメソッドにまとめるか、Ordered または @Order を介してアスペクトレベルでオーダーできる個別の @Aspect クラスにアドバイスをリファクタリングすることを検討してください。