1. 소개

이 튜토리얼에서는 Spring AOP 측면을 사용하여 메서드의 매개변수, 인수 및 어노테이션에 대한 모든 정보를 얻는 방법을 보여줍니다 .

2. Maven 의존성

pom.xmlSpring Boot AOP Starter 라이브러리 의존성 을 추가하여 시작하겠습니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3. Pointcut 어노테이션 만들기

AccountOperation 어노테이션을 만들어 보겠습니다 . 명확히하기 위해, 우리의 관점에서 포인트 컷으로 사용할 것입니다 :

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

포인트 컷을 정의하기 위해 어노테이션 생성은 필수가 아닙니다. 즉, Spring AOP에서 제공하는 포인트 컷 정의 언어를 사용하여 클래스의 특정 메소드, 일부 접두어로 시작하는 메소드 등과 같은 다른 포인트 컷 유형을 정의 할 수 있습니다.

4. 예제 서비스 만들기

4.1. Account class

accountNumber잔액 속성을 사용하여 계정 POJO를 생성 해 보겠습니다 . 서비스 메서드에서 메서드 인수로 사용합니다.

public class Account {

    private String accountNumber;
    private double balance;

    // getter / setters / toString
}

4.2. Service class

이제 우리 측면에서 메서드의 정보를 얻을 수 있도록 @AccountOperation 어노테이션으로 어노테이션을 다는 두 가지 메서드를 사용하여 BankAccountService 클래스를 만들어 보겠습니다 . (가) 주 철회 방법은 체크 예외가 발생 WithdrawLimitException를 우리는 방법에 의해 던져진 예외에 대한 정보를 얻을 수있는 방법을 보여줍니다.

또한 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. Aspect 정의

BankAccountService 에서 호출 된 관련 메서드에서 필요한 모든 정보를 가져 오기 위해 BankAccountAspect만들어 보겠습니다 .

@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을 어노테이션으로 정의 했으므로 BankAccountServicegetBalance 메서드 AccountOperation으로 어노테이션 처리 되지 않았으므로 aspect는이를 가로 채지 않습니다.

이제 측면의 각 부분을 자세히 분석하고 BankAccountService 메서드를 호출 할 때 콘솔에 표시되는 내용을 살펴 보겠습니다 .

5.1. 메서드 서명에 대한 정보 얻기

메서드 서명 정보를 얻으려면 JoinPoint 개체 에서 MethodSignature 를 검색해야 합니다.

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

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. 메소드 어노테이션에 대한 정보 얻기

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

이제 withdraw () 테스트를 다시 실행하고 결과를 확인하겠습니다.

Account operation annotation: @com.baeldung.method.info.AccountOperation(operation=withdraw)
Account operation value: withdraw

5.4. 추가 정보 얻기

반환 유형, 수정 자 및 throw하는 예외 (있는 경우)와 같은 메서드에 대한 추가 정보를 얻을 수 있습니다.

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

이제 withdraw () 메서드가 정의 된 인출 제한을 초과 하도록 만드는 새 테스트 withdrawWhenLimitReached생성 해 보겠습니다 .

@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  어노테이션 이 없기 때문에 aspect에서 가로 채지 않습니다 .

@Test
void getBalance() {
    bankAccountService.getBalance();
}

이 테스트를 실행할 때 예상대로 콘솔에 출력이 없습니다.

6. 결론

이 기사에서 우리는 Spring AOP 측면을 사용하여 메소드에 대해 사용 가능한 모든 정보를 얻는 방법을 보았다. 포인트 컷을 정의하고, 정보를 콘솔에 출력하고, 테스트 실행 결과를 확인함으로써 그렇게했습니다.

애플리케이션의 소스 코드는 GitHub에서 사용할 수 있습니다 .