Spring Advanced

AOP와 @Aspect, @Around

cwchoiit 2023. 12. 29. 09:36
728x90
반응형
SMALL
728x90
SMALL

AOP(Aspect Oriented Programming)는 번역하면 관점 지향 프로그래밍으로 해석할 수 있고, 이 관점 지향 프로그래밍이란 애플리케이션에서 핵심적인 관점과 부가적인 관점을 분리하여 각각을 모듈화하여 개발하는 방법이다. 그리고 이런 관점 지향 프로그래밍을 위해 지금까지 프록시를 만들고 프록시를 어떻게 쓰는지 어떤 의도를 가지고 프록시 패턴과 데코레이터 패턴으로 나뉘어지는지 등 공부를 했다. 그에 대한 내용은 이전 포스팅을 참고하자. 

 

스프링에서는 어드바이저를 스프링 빈으로 등록만 하면 자동으로 스프링이 해당 어드바이저의 포인트컷을 가져다가 프록시가 적용되어야 할 스프링 빈에 알아서 적용을 해주는데 이 어드바이저를 스프링 빈으로 등록하는 과정보다 더 간단하고 더 깔끔하게 프록시를 적용할 수 있는 방법이 있다. 이는 AspectJ 프로젝트에서 제공하는 @Aspect 애노테이션을 사용하는 것이다.

 

AOP에 대해서는 이후 포스팅에서 더 자세히 알아보도록 하고 @Aspect를 이용해서 스프링 빈에 프록시를 적용하는 방법을 먼저 알아보자. 

 

@Aspect를 이용해서 프록시 적용하기

아주 간단하다. 클래스에 @Aspect 애노테이션을 달고, 어드바이스가 될 메서드에 @Around 애노테이션을 달면 된다.

LogTraceAspect.java

package com.example.advanced.app.proxy.config.v6_aop.aspect;

import com.example.advanced.trace.TraceStatus;
import com.example.advanced.trace.logtrace.LogTrace;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;


/**
 * @Aspect 가 붙어있는 녀석이 빈으로 등록되어 있으면 자동 프록시 생성기인 AnnotationAwareAspectJAutoProxyCreator 이 녀석이
 * @Aspect 가 붙어있는 이 녀석을 Advisor 로 만들어 준다. 이 때 Pointcut 은 @Around 가 달려있는 녀석이 포인트컷이 되고
 * 그 안 로직이 Advice 가 된다. 그리고 그 하나가 Advisor 가 된다. 만약 이 @Aspect 가 붙은 녀석이 @Around 가 여러개 있으면 Advisor 도 여러개가 되는 것.
 * 왜냐하면 @Around = 포인트컷, @Around 내부 로직 = 어드바이스, 포인트컷 + 어드바이스 = 어드바이저 이기 때문에 이게 여러개 있으면 어드바이저도 여러개가 생기는 것.
 * */
@Slf4j
@Aspect
public class LogTraceAspect {

    private final LogTrace logTrace;

    public LogTraceAspect(LogTrace logTrace) {
        this.logTrace = logTrace;
    }

    @Around("execution(* com.example.advanced.app.proxy.version..*(..))") // Pointcut
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        // Advice 로직
        TraceStatus status = null;

        try {
            // Ex) "OrderController.request()"
            String message = joinPoint.getSignature().toShortString();

            status = logTrace.begin(message);

            Object result = joinPoint.proceed();

            logTrace.end(status);
            return result;
        } catch (Exception e) {
            logTrace.exception(status, e);
            throw e;
        }
    }
}

 

이렇게 Aspect 클래스를 하나 만들면 되는데, 한가지 더 알아야 할 내용은 @Around 애노테이션에 작성하는 값이다. 이 값은 포인트컷 표현식인데, 지금은 execution의 괄호'()' 안에 있는 표현식에 해당하는 메서드들만 프록시 적용 대상이다라고 생각하면 된다. 괄호 안의 값을 설명하자면,

  • *: 모든 반환 타입을 의미. (원래 맨 앞에 접근 제어자가 가장 먼저인데 생략이 가능하다. 그래서 한 개만 있는 경우 반환 타입을 나타낸다)
  • com.example.advanced.app.proxy.version..: com.example.advanced.app.proxy.version 패키지와 그 하위 모든 패키지(..)를 나타낸다.
  • *(..): 모든 메서드(*)의 모든 파라미터(..)를 의미한다. 

그래서 결론적으로 * com.example.advanced.app.proxy.version..*(..) 은 해당 패키지부터 그 하위 모든 패키지의 모든 반환 타입의 모든 메서드의 어떠한 파라미터도 상관없이 해당되는 메서드들을 가리킨다.

 

그리고 해당 메서드의 파라미터를 보면 'ProceedingJoinPoint joinpoint'를 받는다. 이건 예전 포스팅에서 어드바이스를 만들 때 'MethodInvocation invocation'과 유사하다. 내부에 실제 호출 대상, 전달 인자, 그리고 어떤 객체와 어떤 메서드가 호출되었는지 정보가 포함되어 있다.

 

그래서 joinpoint.proceed()를 호출하는 게 실제 호출 대상(target)을 호출하는 것이다. 여기서 실제 호출 대상을 이해하지 못한다면 이전 포스팅에서 프록시 관련 내용을 먼저 이해해야 한다. 모든 프록시는 실제 호출 대상을 가지고 있어야 한다.

 

 

그러면 어떻게 스프링이 @Aspect 애노테이션이 붙은 클래스를 가져와서 어드바이저를 만들고 프록시를 입혀줄까?

정답은 스프링의 '자동 프록시 생성기(AnnotationAwareAspectJAutoProxyCreator)'에 있다.

 

@Aspect를 어드바이저로 변환하는 과정

우선 스프링의 자동 프록시 생성기는 2가지 일을 한다.

  • 스프링 빈으로 등록된 어드바이저를 가져와서 프록시 적용 대상 판단 후 적용
  • @Aspect를 보고 어드바이저로 변환하고 프록시 적용 대상 판단 후 적용

그래서 전체적인 흐름을 보면 다음과 같다.

 

 

1. 생성: 스프링이 빈으로 등록될 객체를 생성한다.(@Bean, 컴포넌트 스캔 모두 포함)

2. 전달: 생성된 객체를 빈 저장소에 등록하기 전 빈 후처리기에 전달한다.

3. 모든 Advisor 빈 조회: 스프링 컨테이너에서 Advisor 빈을 모두 조회한다.

3-1. 모든 @Aspect 빈 조회: @Aspect 어드바이저 빌더 내부에 저장된 Advisor를 모두 조회한다. (사실 이 과정 전에 먼저 @Aspect 애노테이션이 달려있는 모든 클래스를 찾고 어드바이저로 만든 후 @Aspect 어드바이저 빌더에 저장하는 과정이 생략되어 있다)

4. 프록시 적용 대상 체크: 앞서 3, 3-1 에서 조회한 Advisor에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다. 이 때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭해본다. 그래서 조건이 하나라도 만족하면 프록시 적용 대상이 된다. 예를 들어, 메서드 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 된다.

5. 프록시 생성: 프록시 적용 대상이면 프록시를 생성하고 프록시를 반환한다. 그래서 프록시를 스프링 빈으로 등록한다. 만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록한다.

6. 빈 등록: 반환된 객체는 스프링 빈으로 등록된다.

 

 

정리를 하자면

@Aspect를 사용해서 이전 작업과는 비교도 안되게 편리하게 프록시를 적용할 수 있다. @Aspect로 등록한 클래스를 스프링이 자동으로 만들어주는 자동 프록시 생성기 빈 후처리기를 통해 알아서 포인트컷의 조건을 기반으로 프록시를 만들어준다. 이제 AOP에 대한 깊은 이해를 가져보는 시간이 필요하다.

 

728x90
반응형
LIST

'Spring Advanced' 카테고리의 다른 글

AOP (Part.2)  (0) 2024.01.02
AOP(Aspect Oriented Programming)  (0) 2023.12.29
빈 후처리기(BeanPostProcessor)  (2) 2023.12.27
Advisor, Advice, Pointcut  (0) 2023.12.15
스프링이 지원하는 ProxyFactory  (0) 2023.12.14