Spring

[Spring] @Aspect

이덩우 2024. 3. 7. 17:37

이전 포스팅에서는 스프링 애플리케이션에서 자동 프록시 생성기(AutoProxyCreator)를 통해 프록시를 적용하기위해 필요한 어드바이저를 직접 생성해 스프링 빈으로 등록해주는 과정을 거쳤다.

 

스프링은 `@Aspect` 애노테이션으로 매우 편리하게 포인트컷과 어드바이스로 구성되어있는 어드바이저 생성 기능을 지원한다!

@Aspect

@Slf4j
@Aspect
public class LogTraceAspect {
    private final LogTrace logTrace;

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

    @Around("execution(* hello.proxy.app..*(..))")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        TraceStatus status = null;
        try {
            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()`: 해당 애노테이션 값에 포인트컷 표현식을 넣는다. 표현식은 AspectJ 표현식을 사용, 그리고 해당 메서드 로직이 어드바이스 로직이 된다.
  • `ProceedingJoinPoint` : 어드바이스를 직접 만들 때의 `MethodInvocation`과 유사한 역할을 한다. 실제 target, args, method 등의 정보가 포함되어있다.

 

이제 스프링 빈으로 등록해보자.

@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AopConfig {

    @Bean
    public LogTraceAspect logTraceAspect(LogTrace logTrace) {
        return new LogTraceAspect(logTrace);
    }
}

어드바이저를 생성해 직접 등록하는게 아니라 단순히 @Aspect 애노테이션이 붙은 클래스를 스프링 빈으로 등록해주면 된다.

그러면 자동 프록시 생성시가 스프링 빈으로 등록된 어드바이저를 통해 프록시 객체를 생성할 뿐만 아니라, *@Aspect가 붙어있는 스프링 빈도 찾아서 어드바이저로 만들어준다.*

자동 프록시 생성기 작동 과정

 

 

이전에 직접 어드바이저를 스프링 빈으로 비교하는 방식과 비교해볼까?

@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AutoProxyConfig {
    @Bean
    public Advisor advisor3(LogTrace logTrace) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))");

        LogTraceAdvice advice = new LogTraceAdvice(logTrace);
        return new DefaultPointcutAdvisor(pointcut, advice);
    }
}

어드바이저를 명시적으로 만들어 등록하는 모습이다.

`@Aspect`를 활용하는게 더욱 *추상화*된 모습이라고 할 수 있다. 사용하는 입장에서는 단순히 @Aspect 애노테이션 붙이고, 어디에 적용할건지(포인트컷) 또 어떤 부가 기능을 적용할건지만 하나의 클래스에 작성하면 되기 때문이다.

실무에서 프록시를 적용할 때도 대부분 @Aspect를 활용한 방식을 사용한다.!

 

 

출처 : 인프런, 김영한의 스프링 핵심 원리 - 고급편