知識庫 / Spring / Spring Boot RSS 訂閱

獲取 Spring AOP 建議方法信息

Spring Boot
HongKong
5
12:42 PM · Dec 06 ,2025

1. 簡介

在本教程中,我們將演示如何使用 Spring AOP 方面,獲取方法簽名、參數和註解等所有信息。

2. Maven 依賴

首先,我們在 pom.xml 中添加 Spring Boot AOP Starter 及其 commons-rng-simple 庫的依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-rng-simple</artifactId>
</dependency>

3. 創建我們的 Pointcut 註解

讓我們創建一個 AccountOperation 註解。 澄清一下,我們將使用它作為我們方面中的 pointcut:

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

請注意,定義切入點並不強制創建標註。換句話説,我們可以使用 Spring AOP 提供的切入點定義語言,通過定義類中某些方法的切入點、以某個前綴開頭的某些方法等其他切入點類型。

4. 創建我們的示例服務

4.1. 賬户類

讓我們創建一個名為 Account 的 POJO,其中包含 accountNumberbalance 屬性。我們將使用它作為我們服務方法中的方法參數:

public class Account {

    private String accountNumber;
    private double balance;

    // getter / setters / toString
}

4.2. 服務類

現在,我們創建一個名為 BankAccountService 的類,併為其定義兩個帶有 @AccountOperation 註解的方法,以便我們可以通過切面獲取這些方法的詳細信息。請注意,withdraw 方法拋出一個檢查異常 WithdrawLimitException,用於演示如何獲取方法拋出的異常信息。我們將使用 Apache Commons rng-simple 庫中的 UniformRangeProvider 來模擬銀行賬户的餘額。

此外,請注意,getBalance 方法沒有 AccountOperation 註解,因此不會被切面攔截。

@Component
public class BankAccountService {

    private final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();

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

請注意,我們定義了切片為註解,因此 BankAccountService 中的 getBalance 方法未帶有 AccountOperation 註解,所以切片不會攔截它。

現在,讓我們詳細分析切片的各個部分,並在調用 BankAccountService 方法時查看控制枱輸出。

5.1. 獲取方法簽名信息

為了獲取方法簽名信息,我們需要從 <a href="https://www.javadoc.io/doc/org.aspectj/aspectjrt/latest/org/aspectj/lang/reflect/MethodSignature.html"><em >MethodSignature</em></a > 對象中檢索信息。同時,還需要從JoinPoint 對象中獲取相關數據。

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

讓我們通過調用 deposit 方法在 BankAccountService 中進行操作:

@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. 獲取方法註解信息

我們可以通過使用 getAnnotation() 方法來獲取註解信息,該方法位於 Method 類中:

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

當運行此測試時,控制枱沒有輸出,正如我們預期的那樣。

6. 結論

在本文中,我們學習瞭如何使用 Spring AOP 方面獲取方法的所有可用信息。我們通過定義一個切入點、將信息打印到控制枱以及檢查測試運行結果來實現這一目標。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.