1. Introduction
1.绪论
In this tutorial, we’ll show you how to get all the information about a method’s signature, arguments, and annotations, using a Spring AOP aspect.
在本教程中,我们将向您展示如何使用Spring AOP方面来获取关于方法的签名、参数和注释的所有信息。
2. Maven Dependencies
2.Maven的依赖性
Let’s start by adding Spring Boot AOP Starter library dependency in the pom.xml:
让我们开始添加Spring Boot AOP Starter库的依赖性,在pom.xml。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3. Creating Our Pointcut Annotation
3.创建我们的指针式注释
Let’s create an AccountOperation annotation. To clarify, we’ll use it as the pointcut in our aspect:
让我们创建一个AccountOperation注解。为了清楚起见,我们将把它作为我们的方面的指向标。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccountOperation {
String operation();
}
Note that creating an annotation is not mandatory for defining a pointcut. In other words, we can define other pointcuts types like certain methods in a class, methods starting with some prefix, etc., by using the pointcut definition language provided by Spring AOP.
注意,创建注解并不是定义一个pointcut的必须条件。换句话说,我们可以通过使用Spring AOP提供的pointcut定义语言来定义其他的pointcut类型,如类中的某些方法、以某些前缀开头的方法等等。
4. Creating Our Example Service
4.创建我们的服务实例
4.1. Account Class
4.1.账户类别
Let’s create an Account POJO with accountNumber and balance properties. We’ll use it as the method argument in our service methods:
让我们创建一个带有accountNumber和balance属性的Account POJO。我们将在我们的服务方法中使用它作为方法参数。
public class Account {
private String accountNumber;
private double balance;
// getter / setters / toString
}
4.2. Service Class
4.2.服务等级
Let’s now create the BankAccountService class with two methods we annotate with @AccountOperation annotation so we can get the information of the methods in our aspect. Note the withdraw method throws a checked exception WithdrawLimitException to demonstrate how we can get the information about the exceptions thrown by a method.
现在让我们创建BankAccountService类,其中有两个方法我们用@AccountOperation注解,所以我们可以在我们的方面获得方法的信息。请注意withdraw方法抛出了一个检查过的异常WithdrawLimitException,以证明我们如何获得一个方法抛出的异常信息。
Also, note the getBalance method doesn’t have the AccountOperation annotation, so it won’t be intercepted by the aspect:
另外,注意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. Defining Our Aspect
5.界定我们的方面
Let’s create a BankAccountAspect to get all the necessary information from the related methods called in our BankAccountService:
让我们创建一个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());
}
}
Note we defined our pointcut as an annotation, so as the getBalance method in our BankAccountService is not annotated with AccountOperation, the aspect won’t intercept it.
请注意,我们将我们的切点定义为注解,所以在我们的BankAccountService中的getBalance方法没有被注解为AccountOperation,该方面将不会拦截它。
Let’s now analyze each part of our aspect in detail and look at what we get in the console when calling the BankAccountService methods.
现在让我们详细分析一下我们方面的每个部分,看看在调用BankAccountService方法时,我们在控制台得到什么。
5.1. Getting the Information About Method Signature
5.1.获取关于方法签名的信息
To be able to get our method signature information, we need to retrieve the MethodSignature from the JoinPoint object:
为了能够获得我们的方法签名信息,我们需要从MethodSignature 对象中检索到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());
Let’s now call the withdraw() method of our service:
现在让我们调用我们服务的withdraw()方法。
@Test
void withdraw() {
bankAccountService.withdraw(account, 500.0);
assertTrue(account.getBalance() == 1500.0);
}
After running the withdraw() test, we can now see on the console the following results:
运行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. Getting the Information About Arguments
5.2.获取有关参数的信息
To retrieve the information about the method arguments, we can use the MethodSignature object:
为了检索方法参数的信息,我们可以使用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()));
Let´s try this by calling the deposit method in BankAccountService:
让我们通过调用BankAccountService中的deposit方法进行尝试。
@Test
void deposit() {
bankAccountService.deposit(account, 500.0);
assertTrue(account.getBalance() == 2500.0);
}
This is what we see on the console:
这就是我们在控制台看到的情况。
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. Getting the Information About Method Annotations
5.3.获取方法注解的信息
We can get the information about an annotation by using the getAnnotation() method of the Method class:
我们可以通过使用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());
Let’s now re-run our withdraw() test and check what we get:
现在让我们重新运行我们的withdraw()测试,看看我们得到了什么。
Account operation annotation: @com.baeldung.method.info.AccountOperation(operation=withdraw)
Account operation value: withdraw
5.4. Getting the Additional Information
5.4.获得附加信息
We can get some additional information about our methods, like their returning type, their modifiers, and what exceptions they throw, if any:
我们可以得到一些关于我们的方法的额外信息,比如它们的返回类型,它们的修饰语,以及它们抛出的异常,如果有的话。
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));
Let’s now create a new test withdrawWhenLimitReached that makes the withdraw() method exceed its defined withdraw limit:
现在让我们创建一个新的测试withdrawWhenLimitReached,使withdraw()方法超过其定义的提款限额。
@Test
void withdrawWhenLimitReached()
{
Assertions.assertThatExceptionOfType(WithdrawLimitException.class)
.isThrownBy(() -> bankAccountService.withdraw(account, 600.0));
assertTrue(account.getBalance() == 2000.0);
}
Let´s now check the console output:
现在让我们检查一下控制台的输出。
returning type: void
method modifier: public
exception type: class com.baeldung.method.info.WithdrawLimitException
Our last test will be useful to demonstrate the getBalance() method. As we previously said, it’s not intercepted by the aspect because there is no AccountOperation annotation in the method declaration:
我们的最后一个测试将有助于演示getBalance() 方法。正如我们之前所说的,它没有被方面拦截,因为在方法声明中没有AccountOperation注释。
@Test
void getBalance() {
bankAccountService.getBalance();
}
When running this test, there is no output in the console, as we expected should be the case.
当运行这个测试时,控制台中没有输出,正如我们预期的那样,应该是这样。
6. Conclusion
6.结语
In this article, we saw how to get all the available information about a method using a Spring AOP aspect. We did that by defining a pointcut, printing out the information into the console, and checking the results of running the tests.
在这篇文章中,我们看到了如何使用Spring AOP方面来获得关于一个方法的所有可用信息。 我们通过定义一个pointcut,将信息打印到控制台,并检查运行测试的结果来做到这一点。
The source code for our application is available over on GitHub.
我们的应用程序的源代码可在GitHub上获得。