Автор оригинала: Sallo Szrajbman.
1. введение
В этом уроке мы покажем вам, как получить всю информацию о сигнатуре, аргументах и аннотациях метода, используя аспект Spring AOP.
2. Зависимости Maven
Давайте начнем с добавления Spring Boot APP Starter библиотечной зависимости в pom.xml :
org.springframework.boot spring-boot-starter-aop
3. Создание Нашей Аннотации Точечного Разреза
Давайте создадим Операцию учетной записи аннотацию. Чтобы уточнить, мы будем использовать его в качестве точечного разреза в нашем аспекте:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AccountOperation { String operation(); }
Обратите внимание, что создание аннотации не является обязательным для определения точечного разреза. Другими словами, мы можем определить другие типы точечных вырезов, такие как определенные методы в классе, методы, начинающиеся с некоторого префикса и т. Д., Используя язык определения точечных вырезов, предоставляемый Spring AOP.
4. Создание Нашего Примера Сервиса
4.1. Класс учетной записи
Давайте создадим Учетную запись POJO с номером счета и балансом свойствами. Мы будем использовать его в качестве аргумента метода в наших методах обслуживания:
public class Account { private String accountNumber; private double balance; // getter / setters / toString }
4.2. Класс обслуживания
Теперь давайте создадим класс Служба банковских счетов с двумя методами, которые мы аннотируем с помощью @AccountOperation аннотации, чтобы мы могли получить информацию о методах в нашем аспекте. Обратите внимание, что метод withdraw/| создает проверенное исключение Исключение ограничения вывода , чтобы продемонстрировать, как мы можем получить информацию об исключениях, создаваемых методом.
Кроме того, обратите внимание, что метод getBalance не имеет аннотации Account Operation , поэтому он не будет перехвачен аспектом:
@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()); } }
Примечание.Мы определили наш pointcut как аннотацию, так как метод getBalance в вашем сервисе банковских счетов не аннотирован AccountOperation, аспект не будет перехватывать его.
Теперь давайте подробно проанализируем каждую часть нашего аспекта и посмотрим, что мы получаем в консоли при вызове методов BankAccountService .
5.1. Получение Информации О Сигнатуре Метода
Чтобы получить информацию о сигнатуре вашего метода, нам нужно получить сигнатуру метода | из объекта 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));
Теперь давайте создадим новый тест вывод средств при достижении лимита , который заставляет метод вывод() превышать установленный лимит вывода средств:
@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 () . Как мы уже говорили ранее, он не перехватывается аспектом, потому что в объявлении метода нет аннотации Account Operation :
@Test void getBalance() { bankAccountService.getBalance(); }
При запуске этого теста в консоли нет вывода, как мы и ожидали.
6. Заключение
В этой статье мы рассмотрели, как получить всю доступную информацию о методе, использующем аспект Spring AOP. Мы сделали это, определив точечный разрез, распечатав информацию в консоли и проверив результаты выполнения тестов.
Исходный код нашего приложения доступен на GitHub .