Рубрики
Без рубрики

Получите информацию о методе в Spring AOP

Автор оригинала: 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 .