在Spring AOP中獲取Advised方法信息

    1.簡介

    在本教程中,我們將向您展示如何使用Spring AOP方面獲取有關方法的簽名,參數和註釋的所有信息。

    2. Maven依賴

    讓我們從pom.xml添加Spring Boot AOP Starter庫依賴關係開始:

    <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();
    
     }
    

    請注意,創建註釋對於定義切入點不是強制性的。換句話說,我們可以使用Spring AOP提供的切入點定義語言來定義其他切入點類型,例如類中的某些方法,以某些前綴開頭的方法等。

    4.創建示例服務

    4.1。帳戶類別

    讓我們創建一個帶有accountNumberbalance屬性的Account POJO。我們將其用作服務方法中的方法參數:

    public class Account {
    
    
    
     private String accountNumber;
    
     private double balance;
    
    
    
     // getter / setters / toString
    
     }

    4.2。服務等級

    現在,讓我們使用兩個用@AccountOperation批註進行批註的方法來創建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:調用的相關方法中獲取所有必要的信息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,註釋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));

    現在讓我們創建一個新的withdrawWhenLimitReached測試,該測試使withdraw()方法超出其定義的提現限制:

    @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();
    
     }

    運行此測試時,控制台中沒有輸出,正如我們期望的那樣。

    六,結論

    在本文中,我們了解瞭如何獲取有關使用Spring AOP方面的方法的所有可用信息。為此,我們定義了一個切入點,將信息打印到控制台中,並檢查運行測試的結果。