@Aspect 프록시 - 적용

스프링 애플리케이션에 프록시를 적용하려면 포인트컷과 어드바이스로 구성되어있는 어드바이저를 만들어서 스프링빈으로 등록하면 됨. 나머지는 자동 프록시 생성기가 처리해주는 것 까지다뤘었다.

스프링이 지원해주는건 여기서 끝이 아니다. @Aspect 애너테이션을 이용해 포인트컷과 어드바이스로 구성되어있는 어드바이저를 더 쉽게 생성할 수 있다.

@Aspect 는 AOP라이브러리인 AspectJ에서 지원하는 애너테이션임. 스프링은 이걸 이용하고 있다. AOP와 AspectJ는 나중에 다시 더 다룰 것.

다음은 @Aspect + @Around 이용해 어드바이저를 만드는 코드이다.

@Aspect
@Slf4j
public class LogTraceAspect {
    
    private final LogTrace logTrace;
    
    public LogTraceAspect(LogTrace logTrace) {
        this.logTrace = logTrace;
    }
    
    //@Around가 포인트 컷의 역할을 함.
    //포인트컷이 적용된 메서드가 어드바이스의 역할을 함
    @Around("execution(* hello.proxy.app..*(..))")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
       //여기서부터 어드바이스 로직을 짜면 됨.
        // 로그 추적 시작
        TraceStatus status = null;
        try {
            
            // 로그 추적 시작
//            Method method = invocation.getMethod();
//            String message = method.getDeclaringClass().getSimpleName() + "."
//                + method.getName();
            
            // joinPoint를 통해 메서드 정보 가져오기
            String message = joinPoint.getSignature().toShortString();
            status = logTrace.begin(message);
            
            // target의 핵심 로직 호출
//            Object result = invocation.proceed();
            
            
            // ProceedingJoinPoint를 사용하여 실제 메서드 호출
            Object result = joinPoint.proceed();
            
            logTrace.end(status);
            return result;
        } catch (Exception e){
            log.error("Exception in LogTraceBasicHandler", e);
            if (status != null) {
                logTrace.exception(status, e);
            }
            throw e; // 예외를 다시 던져줘야 함
        }
    }

@Aspect 는 애노테이션기반 프록시를 사용할때 클래스에 붙여줌.

@Around 는 포인트컷의 표현식을 넣어주면 됨. 표현식은 AspectJ표현식을 사용함. @Around가 붙은 메서드는 어드바이스가 된다.

ProceedingJoinPoint 클래스는 이전에 Advice에서 살펴봤던 MethodInvocation 클래스와 유사한 기능을 한다.

기억이 안난다면 다음을 참고. MethodInterceptor 가 인자로 주입받는 클래스가 MethodInvocation 이다.

프록시 팩토리

내부에 실제 호출 대상(target), 전달 인자(args), 그리고 추가 메타 정보들이 담겨있다.

joinPoint.proceed()를 통해 실제 호출대상의 메서드를 호출 할 수 있다.

위 클래스를 사용해보자. config파일을 만들어서 빈으로 등록해주면 된다.

@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AopConfig {
    
    @Bean
    public LogTraceAspect logTraceAspect(LogTrace logTrace) {
        return new LogTraceAspect(logTrace);
    }
}