1. 序章

このチュートリアルでは、 Spring AOP アスペクトを使用して、メソッドのシグネチャ、引数、およびアノテーションに関するすべての情報を取得する方法を示します。

2. Mavenの依存関係

[X21X]pom.xmlにSpring BootAOPStarterライブラリの依存関係を追加することから始めましょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3. ポイントカット注釈の作成

AccountOperationアノテーションを作成しましょう。 明確にするために、私たちはそれを私たちの側面のポイントカットとして使用します:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccountOperation {
    String operation();
}

ポイントカットの定義には注釈の作成は必須ではないことに注意してください。つまり、ポイントカット定義を使用して、クラス内の特定のメソッド、プレフィックスで始まるメソッドなど、他のポイントカットタイプを定義できます。 SpringAOPが提供する言語。

4. サンプルサービスの作成

4.1. アカウントクラス

accountNumberおよびbalanceプロパティを使用してAccountPOJOを作成しましょう。 これをサービスメソッドのメソッド引数として使用します。

public class Account {

    private String accountNumber;
    private double balance;

    // getter / setters / toString
}

4.2. サービスクラス

次に、@AccountOperationアノテーションを付けた2つのメソッドを使用してBankAccountServiceクラスを作成し、アスペクトのメソッドの情報を取得できるようにします。 withdraw メソッドは、チェックされた例外 WithdrawLimitException をスローして、メソッドによってスローされた例外に関する情報を取得する方法を示していることに注意してください。

また、getBalanceメソッドにはAccountOperationアノテーションがないため、アスペクトによってインターセプトされないことに注意してください。

@Component
public class BankAccountService {

    @AccountOperation(operation = "deposit")
    public void deposit(Account account, Double amount) {
        account.setBalance(account.getBalance() + amount);
    }

    @AccountOperation(operation = "withdraw")
    public void withdraw(Account account, Double amount) throws WithdrawLimitException {

        if(amount > 500.0) {
            throw new WithdrawLimitException("Withdraw limit exceeded.");
        }

        account.setBalance(account.getBalance() - amount);
    }

    public double getBalance() {
        return RandomUtils.nextDouble();
    }
}

5. 私たちの側面を定義する

BankAccountAspect を作成して、 BankAccountService:で呼び出される関連メソッドから必要なすべての情報を取得しましょう。

@Aspect
@Component
public class BankAccountAspect {

    @Before(value = "@annotation(com.baeldung.method.info.AccountOperation)")
    public void getAccountOperationInfo(JoinPoint joinPoint) {

        // Method Information
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        System.out.println("full method description: " + signature.getMethod());
        System.out.println("method name: " + signature.getMethod().getName());
        System.out.println("declaring type: " + signature.getDeclaringType());

        // Method args
        System.out.println("Method args names:");
        Arrays.stream(signature.getParameterNames())
          .forEach(s -> System.out.println("arg name: " + s));

        System.out.println("Method args types:");
        Arrays.stream(signature.getParameterTypes())
          .forEach(s -> System.out.println("arg type: " + s));

        System.out.println("Method args values:");
        Arrays.stream(joinPoint.getArgs())
          .forEach(o -> System.out.println("arg value: " + o.toString()));

        // Additional Information
        System.out.println("returning type: " + signature.getReturnType());
        System.out.println("method modifier: " + Modifier.toString(signature.getModifiers()));
        Arrays.stream(signature.getExceptionTypes())
          .forEach(aClass -> System.out.println("exception type: " + aClass));

        // Method annotation
        Method method = signature.getMethod();
        AccountOperation accountOperation = method.getAnnotation(AccountOperation.class);
        System.out.println("Account operation annotation: " + accountOperation);
        System.out.println("Account operation value: " + accountOperation.operation());
    }
}

ポイントカットを注釈として定義したことに注意してください。これは、BankAccountServicegetBalanceメソッドにAccountOperationの注釈が付けられていないため、アスペクトがそれをインターセプトしないためです。

ここで、アスペクトの各部分を詳細に分析し、BankAccountServiceメソッドを呼び出したときにコンソールに何が表示されるかを見てみましょう。

5.1. メソッドシグネチャに関する情報の取得

メソッドシグネチャ情報を取得できるようにするには、JoinPointオブジェクトからMethodSignatureを取得する必要があります。

MethodSignature signature = (MethodSignature) joinPoint.getSignature();

System.out.println("full method description: " + signature.getMethod());
System.out.println("method name: " + signature.getMethod().getName());
System.out.println("declaring type: " + signature.getDeclaringType());

ここで、サービスの withdraw()メソッドを呼び出します。

@Test
void withdraw() {
    bankAccountService.withdraw(account, 500.0);
    assertTrue(account.getBalance() == 1500.0);
}

withdraw()テストを実行した後、コンソールに次の結果が表示されます。

full method description: public void com.baeldung.method.info.BankAccountService.withdraw(com.baeldung.method.info.Account,java.lang.Double) throws com.baeldung.method.info.WithdrawLimitException
method name: withdraw
declaring type: class com.baeldung.method.info.BankAccountService

5.2. 引数に関する情報の取得

メソッド引数に関する情報を取得するには、MethodSignatureオブジェクトを使用できます。

System.out.println("Method args names:");
Arrays.stream(signature.getParameterNames()).forEach(s -> System.out.println("arg name: " + s));

System.out.println("Method args types:");
Arrays.stream(signature.getParameterTypes()).forEach(s -> System.out.println("arg type: " + s));

System.out.println("Method args values:");
Arrays.stream(joinPoint.getArgs()).forEach(o -> System.out.println("arg value: " + o.toString()));

BankAccountServicedepositメソッドを呼び出してこれを試してみましょう。

@Test
void deposit() {
    bankAccountService.deposit(account, 500.0);
    assertTrue(account.getBalance() == 2500.0);
}

これがコンソールに表示されるものです。

Method args names:
arg name: account
arg name: amount
Method args types:
arg type: class com.baeldung.method.info.Account
arg type: class java.lang.Double
Method args values:
arg value: Account{accountNumber='12345', balance=2000.0}
arg value: 500.0

5.3. メソッドアノテーションに関する情報の取得

MethodクラスのgetAnnotation()メソッドを使用して、注釈に関する情報を取得できます。

Method method = signature.getMethod();
AccountOperation accountOperation = method.getAnnotation(AccountOperation.class);
System.out.println("Account operation annotation: " + accountOperation);
System.out.println("Account operation value: " + accountOperation.operation());

withdraw()テストを再実行して、何が得られるかを確認しましょう。

Account operation annotation: @com.baeldung.method.info.AccountOperation(operation=withdraw)
Account operation value: withdraw

5.4. 追加情報の取得

メソッドに関する追加情報を取得できます。たとえば、戻り値の型、修飾子、スローされる例外(存在する場合)などです。

System.out.println("returning type: " + signature.getReturnType());
System.out.println("method modifier: " + Modifier.toString(signature.getModifiers()));
Arrays.stream(signature.getExceptionTypes())
  .forEach(aClass -> System.out.println("exception type: " + aClass));

ここで、 withdraw()メソッドが定義された引き出し制限を超えるようにする新しいテストwithdrawWhenLimitReachedを作成しましょう。

@Test 
void withdrawWhenLimitReached() 
{ 
    Assertions.assertThatExceptionOfType(WithdrawLimitException.class)
      .isThrownBy(() -> bankAccountService.withdraw(account, 600.0)); 
    assertTrue(account.getBalance() == 2000.0); 
}

次に、コンソール出力を確認しましょう。

returning type: void
method modifier: public
exception type: class com.baeldung.method.info.WithdrawLimitException

最後のテストは、 getBalance()メソッドを示すのに役立ちます。 前に述べたように、メソッド宣言には AccountOperation アノテーションがないため、アスペクトによってインターセプトされません。

@Test
void getBalance() {
    bankAccountService.getBalance();
}

このテストを実行すると、予想どおり、コンソールに出力が表示されません。

6. 結論

この記事では、SpringAOPアスペクトを使用してメソッドに関する利用可能なすべての情報を取得する方法を説明しました。  これを行うには、ポイントカットを定義し、情報をコンソールに出力して、テストの実行結果を確認します。

このアプリケーションのソースコードは、GitHubから入手できます。