본문 바로가기
Spring

[Spring] @Aspect 정리

by 개미가되고싶은사람 2024. 9. 26.

목차

    @Aspect란?

    스프링 프레임워크의 AOP(Aspect-Oriented Programming)를 활용하면 여러 클래스에 걸쳐 공통 기능을 효과적으로 적용할 수 있습니다. 이때 핵심 개념인 어드바이저(Advisor)는 포인트컷(Pointcut)과 어드바이스(Advice)로 구성됩니다.

    1. 포인트컷(Pointcut): 어드바이스를 적용할 메서드를 지정하는 조건입니다. 예를 들어, 특정 패키지의 모든 메서드나 특정 이름을 가진 메서드에 적용할 수 있습니다.
    2. 어드바이스(Advice): 포인트컷에서 정의한 조건에 맞는 메서드가 실행될 때 수행할 작업입니다. 

    스프링에서는 @Aspect 애노테이션을 사용해 손쉽게 어드바이저를 생성할 수 있습니다. 이 애노테이션을 클래스에 적용하면 해당 클래스가 AOP의 어드바이저 역할을 수행하게 됩니다.

    더보기

    스프링은 기본적으로 자동 프록시 생성기(AnnotationAwareAspectJAutoProxyCreator)를 생성에 빈으로 등록합니다.
    이 자동 프록시 생성기로 스프링은 빈으로 등록된 어드바이저를 찾고 스프링 빈들을 자동으로 프록시를 적용합니다. 물론 포인트컷에 조건이 만족하는 경우 프록시를 생성합니다.

    https://velog.io/@flasharrow/Spring-Boot-AOP-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95

     

     

    @Aspect를 통해 어드바이저 생성 예제

    1 Aspect 클래스 정의 

    스프링에서 제공하는 @Aspect 애노이테션으로 통해 어드바이저를 쉽게 생성하기 위해서 @Aspect 애노테이션을 가진 클래스를 생성합니다.

    @Slf4j
    @Aspect
    @RequiredArgsConstructor
    public class LogTraceAspect {
    
    
        @Around("execution(* hello.proxy.app..*(..))") // 포인트컷
        public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { // 어드바이스
            log.info("target={}", joinPoint.getTarget()); //실제 호출 대상(객체)
            log.info("getArgs={}", joinPoint.getArgs()); //전달인자(전달 받은 매개변수)
            log.info("getSignature={}", joinPoint.getSignature()); //join point 시그니처
           
           log.info("LogTraceAspect.execute() 동작");
    
    	   Object result = joinPoint.proceed();
           return result;
        }
    }

     

    어드바이저는 두 가지 주요 요소로 구성되어 있습니다: 포인트컷과 어드바이스입니다. 현재 코드에서는 LogTraceAspect가 @Aspect 애노테이션 덕분에 어드바이저 역할을 하고 있으며, @Around 애노테이션은 포인트컷 역할을 수행합니다.

    또한, @PointCut 애노테이션을 사용할 수도 있지만, @Around, @Before, @After와 같은 다양한 애노테이션을 통해서도 포인트컷 역할을 수행할 수 있습니다. 추가적으로 위에 코드에서는 AspectJ 표현식을 사용하고 있지만, 패키지 범위나 메소드 이름을 기준으로도 포인트컷을 정의할 수 있습니다.

     

     

     

    2 스프링 빈 등록

    @Aspect 애노테이션을 통해 어드바이저를 만들었기 때문에, 이를 스프링 빈으로 등록해야 합니다. 왜냐하면 해당 방법은 스프링이 제공하는 자동 프록시 생성기를 사용하기 때문입니다.

    @Configuration
    public class AopConfig {
    
        @Bean
        public LogTraceAspect logTraceAspect() {
            return new LogTraceAspect();
        }
    }


    @Aspect를 통해 어드바이저가 만들어지는 과정

    1. 자동 프록시 생성: 스프링은 애플리케이션이 시작될 때 자동으로 프록시 생성기를 만들어 호출합니다.
    2. @Aspect 빈 조회: 생성된 프록시 생성기는 스프링 컨테이너에서 @Aspect으로 등록된 빈을 조회합니다.
    3. 어드바이저 생성: 찾은 빈에서 포인트컷(어떤 메서드에 어드바이스를 적용할지 결정하는 정보)과 어드바이스(실제로 실행할 기능)에 정보를 바탕으로 어드바이저를 생성합니다.
    참고로 어드바이저를 생성할 때에는BeanFactoryAspectJAdvisorsBuilder를 통해 생성됩니다.

     

    스프링 AOP를 적용한 프록시 객체 생성 흐름

    1. 스프링 빈 대상이 되는 객체를 생성
    2. 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달되어 추가 작업을 수행하거나 그대로 저장됩니다.
    3. Advisor 빈 조회
      1. 스프링 컨테이너에서 모든 Advisor 객체 빈을 조회
      2. @Aspect 애노테이션이 붙은 클래스를 조회하며, 해당 클래스를 Advisor로 만들어버립니다. 
    4. 3-1, 3-2에서 조회한 Advisor에 PointCut의 조건을 보고 해당 객체가 프록시를 적용할 대상인지 판단합니다. 이때 PointCut 조건에 만족하지 않으면 advice를 적용하지 않고 원래 기능을 수행합니다.
    5. PointCut 조건에 만족하면 advice를 적용해서 프록시 객체를 생성
    6. 자동 프록시 생성기에서 반환된 객체를 스프링 빈으로 등록합니다.

     

    어드바이스 적용 순서

     

    스프링 5.2.7 버전부터는 동일한 @Aspect 내에서 조인 포인트에 대해 어드바이스의 우선순위를 정했습니다.

    1. @Around
    2. @Before
    3. @After
    4. @AfterReturning
    5. @AfterThrowing

    이 순서가 어드바이스가 적용되는 순서입니다. 하지만 호출 순서와 리턴 순서가 다르다는 점을 주의해야 합니다. 즉, 어드바이스가 실행되는 순서와 메서드가 호출된 후 결과를 반환하는 순서는 다릅니다.
    그리고 같은 포인트컷을 사용하는 어드바이스가 @Aspect 안에 두 개 이상 있을 경우, 그 실행 순서는 보장되지 않는 문제가 있습니다. 이 문제를 해결하기 위해서는 Aspect클래스를 세분화하여 @Order를 이용해 우선순위를 지정해 해당 문제를 해결할 수 있습니다.

     

    참고

    [1] - 스프링 핵심 원리 고급편 - 김영한

    [2] - 스프링 공식문서 - https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/advice.html