티스토리 뷰

Spring

[Spring] AOP Aspect에서 어노테이션 정보나 메소드의 파라미터값 가져오는 방법

망나니개발자 2022. 4. 7. 10:00
반응형

Spring 프레임워크를 이용해 공부를 하다보면 AOP를 적용해야 하는 상황이 온다. 그리고 AOP를 적용하다보면 클래스나 메소드에 있는 어노테이션 또는 메소드로 넘겨진 파라미터 값을 필요로 할 때가 있다. 이번에는 이 부분을 어떻게 해야 하는지 살펴보도록 하자.

 

 

 

 

1. AOP Aspect에서 어노테이션 정보나 메소드의 파라미터값 가져오는 방법


[ 메소드의 파라미터 값 가져오기 ]

예를 들어 다음과 같이 클래스 레벨의 어노테이션, 메소드 레벨의 어노테이션이 있는 메소드가 있다고 하자.

@Service
@RequiredArgsConstructor
@Slf4j
@ClassAop
public class HelloService {

    @MethodAop("myValue")
    public String hello(String name) {
        log.info("Hello " + name);
        return name;
    }

}

 

 

그리고 MethodAop를 위한 MethodAspect에서 hello 메소드의 파라미터를 어떻게 하면 꺼낼 수 있는지 살펴보도록 하자.

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class HelloAspect {

    @Around("@annotation(com.example.testing.poly.MethodAop)")
    public Object aspectParameter(final ProceedingJoinPoint joinPoint) throws Throwable {

    }
}

 

 

 

1. 파라미터 이름 목록과 값 목록을 통해 조회

가장 먼저 ProceedingJoinPoint를 통해 파라미터 이름 배열과 파라미터 값 배열을 얻을 수 있다. 그러므로 파라미터 이름의 배열을 순회하여 우리가 원하는 파라미터 이름이 나왔을 때의 인덱스 값을 사용해 파라미터 값 배열로부터 꺼내는 방법이 있다. 이를 구현하면 다음과 같다.

@Around("@annotation(com.example.testing.poly.MethodAop)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint) throws Throwable {
    log.error("[aspectParameter] name: {}", getName(joinPoint, "name"));
    return joinPoint.proceed();
}

private String getName(final ProceedingJoinPoint joinPoint, final String parameterName) {
    final String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
    final Object[] arguments = joinPoint.getArgs();
    for (int i = 0; i < parameterNames.length; i++) {
        if (parameterNames[i].equals(parameterName)) {
            return (String) arguments[i];
        }
    }

    throw new NoSuchParameterException(parameterName);
}

 

 

하지만 이러한 방법은 상당히 번거로우며 다음과 같은 문제를 야기할 수 있다.

  • 파라미터 이름이 없을 때 오류가 발생할 수 있음
  • 파라미터 이름이 변경되었을 때 같이 수정해주어야 하는데, 놓치기 쉬움
  • Aspect 코드마다 중복을 야기할 수 있음
  • 테스트하기 어어려움

 

 

2. 파라미터 값 배열로부터 직접 조회

조금 더 단순한 방법으로 파라미터 값 배열로부터 직접 조회할 수 있다.

@Around("@annotation(com.example.testing.poly.MethodAop)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint) throws Throwable {
    log.error("[aspectParameter] name: {}", joinPoint.getArgs()[0]);
    return joinPoint.proceed();
}

 

 

하지만 위와 같은 코드 역시 아직 다음과 같은 단점이 남아있다.

  • 파라미터가 변경되었을 때 문제가 발생할 수 있음
  • 테스트하기 어려움

 

3. args로 값을 주입받기

이러한 문제를 해결하기 위해 args를 이용해 값을 주입받는 방법이 있다. args는 기본적으로 파라미터 포인트 컷(AOP 적용 조건)에 해당하므로 해당 타입이 순서대로 매칭되지 않으면 AOP가 적용되지 않는다.

@Around("@annotation(com.example.testing.poly.MethodAop) && args(name, ..)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint, final String name) throws Throwable {
    log.error("[aspectParameter] name: {}", name);
    return joinPoint.proceed();
}

 

 

위의 예시에서는 args(name, ..) 포인트 컷이 추가되었고, 메소드의 파라미터로 String name이 추가되었다. 이것은 String 타입이 첫 번째 파라미터로 반드시 존재하고, 그 뒤에 ..는 다음 파라미터로 신경쓰지 않겠다(0..N)는 의미이다. 그러므로 첫 번째 인자로 String 타입이 있어야 해당 AOP가 동작한다. 만약 위의 String name을 Integer name으로 변경하면 에러가 발생하는 것이 아니라 AOP가 적용되지 않는다.

이러한 방식을 이용하면 깔끔하게 필요한 파라미터 값을 주입받을 수 있으며 파라미터 순서가 변경되거나 해도 동작을 하지 않으므로 에러를 발생하지는 않는다. 또한 테스트 코드를 작성하기가 훨씬 수월해진다.

 

 

 

 

[ 메소드의 파라미터 값 가져오기 ]

어노테이션 기반으로 AOP를 개발하면 어노테이션에 있는 값이 필요하여 해당 어노테이션의 값을 조회해야 하는 상황이 생길 수 있다. 어노테이션이 클래스에 있는지 또는 메소드에 있는지에 따라 가져오는 방법이 다르다.

 

 

클래스 어노테이션

클래스 어노테이션을 가져오기 위해서는 @within 또는 @target을 사용해주면 된다.

@Around("@annotation(com.example.testing.poly.MethodAop) && args(name) && @within(annotation)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint, final String name, final ParentAop annotation) throws Throwable {
    log.error("[aspectParameter] name: {}, value: {}", name, annotation.annotationType());
    return joinPoint.proceed();
}

@Around("@annotation(com.example.testing.poly.MethodAop) && args(name) && @target(annotation)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint, final String name, final ParentAop annotation) throws Throwable {
    log.error("[aspectParameter] name: {}, value: {}", name, annotation.annotationType());
    return joinPoint.proceed();
}

 

 

메소드 어노테이션

메소드 어노테이션을 필요로 할 때에는 다음과 같이 가져올 수 있다.

@Around("@annotation(annotation) && args(name)")
public Object aspectParameter(final ProceedingJoinPoint joinPoint, final String name, final MethodAop annotation) throws Throwable {
    log.error("[aspectParameter] name: {}, value: {}", name, annotation.value());
    return joinPoint.proceed();
}

 

 

 

 

보통 위의 정도로 사용법을 알고 있으면 거의 대부분의 상황에서 값을 가져올 수 있다. 물론 위에서는 값을 가져오는 방법 자체를 설명하긴 했지만 자세한 동작 과정도 알 필요가 있다. 그러므로 위의 내용은 AspectJ 표현식이므로 관련 내용도 참고해보도록 하자.

 

 

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함