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,其中包含 accountNumber 和 balance 屬性。我們將使用它作為我們服務方法中的方法參數:
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.BankAccountService5.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.05.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: withdraw5.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 方面獲取方法的所有可用信息。我們通過定義一個切入點、將信息打印到控制枱以及檢查測試運行結果來實現這一目標。