AOP 사용 예제

YH·2023년 3월 7일
0
  1. 가장 기본적인 AOP 사용 예제
@Slf4j
@Aspect
public class AspectV1 {
    @Around("execution(* practice.aop.order..*(..))")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}
  • @Around 안에 있는 값이 '포인트컷'
  • 메소드인 doLog가 '어드바이스'
  • @Aspect는 따로 컴포넌트 스캔이 되지 않음, 따라서 별도로 스프링 빈으로 등록해야 함
    • 스프링 빈 등록 방법
      1. @Bean을 사용하여 직접 등록
    1. @Component를 사용하여 컴포넌트 스캔으로 자동 등록
    2. @Import를 사용, 주로 설정 파일을 추가할 때 사용(@Configuration)
  1. 포인트컷 분리
@Aspect
@Slf4j
public class AspectV2 {

    //practice.aop.order 패키지와 하위 패키지
    @Pointcut("execution(* practice.aop.order..*(..))")
    private void allOrder() {} //pointcut signature

    @Around("allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); //join point signature
        return joinPoint.proceed();
    }
}
  • @Pointcut 안에 포인트컷 표현식으로 값을 넣음
  • 메소드 이름과 파라미터를 합쳐서 '포인트컷 시그니처(Signature)'라고 함, 위 예제에서는 allOrder()가 포인트컷 시그니처
  • 코드 내용은 비워둠
  • @Around 안에 포인트컷 시그니처를 넣어서 사용 가능
  • 다른 Aspect에서 해당 Aspect를 참고하려면 private를 public으로 변경할 것
  1. 어드바이스 추가
@Aspect
@Slf4j
public class AspectV3 {

    //practice.aop.order 패치키와 하위 패키지
    @Pointcut("execution(* practice.aop.order..*(..))")
    private void allOrder() {} //pointcut signature

    //클래스 이름 패턴이 *Service
    @Pointcut("execution(* *..*Service.*(..))")
    private void allService() {}

    @Around("allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); //join point signature
        return joinPoint.proceed();
    }

    @Around("allOrder() && allService()")
    //practice.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
            Object result = joinPoint.proceed();
            log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
            throw e;
        } finally  {
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
        }
    }
}
  • @Around("allOrder() && allService()") 와 같이 포인트컷을 조합할 수 있음,
    조합 시에 &&(AND), ||(OR), !(NOT) 을 사용
  1. 포인트컷 참조
@Slf4j
@Aspect
public class Pointcuts {
    //practice.aop.order 패치키와 하위 패키지
    @Pointcut("execution(* practice.aop.order..*(..))")
    public void allOrder() {} //pointcut signature

    //클래스 이름 패턴이 *Service
    @Pointcut("execution(* *..*Service.*(..))")
    public void allService() {}

    @Pointcut("allOrder() && allService()")
    public void orderAndService(){}
}
@Around("practice.aop.order.aop.Pointcuts.allOrder()")
@Around("practice.aop.order.aop.Pointcuts.orderAndService()")
  • 포인트컷을 공용으로 사용하기 위해 별도의 클래스에 모아두는 것도 가능
  • 외부에서 호출할 때는 public으로 지정 해야 함
  • 포인트컷을 여러 어드바이스에서 함께 사용할 때 해당 방법을 사용하면 효과적
  1. 어드바이스 순서
@Slf4j
public class AspectV5Order {

    @Aspect
    @Order(1)
    public static class LogAspect {
        @Around("practice.aop.order.aop.Pointcuts.allOrder()")
        public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
            ...
        }
    }

    @Aspect
    @Order(2)
    public static class TxAspect {
        @Around("practice.aop.order.aop.Pointcuts.orderAndService()")
        //practice.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
        public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
            ...
        }
    }
}
  • 어드바이스의 순서를 지정하려면 @Aspect 적용 단위로 @Order를 적용해야 함
  • 다만, @Order는 어드바이스 단위가 아닌 클래스 단위로만 적용이 가능 함
    -> 즉, Aspect를 별도의 클래스로 분리 해야 함
profile
하루하루 꾸준히 포기하지 말고

0개의 댓글