Spring

Spring AOP / @Aspect (JoinCut와 Advice)

쭈녁 2024. 1. 12. 23:33

 

AOP 용어

 

- Target : 부가기능이 적용될 타

- Advice : 부가기능을 수행하는 로직

- JoinPoint : Advice 가 적용될 수 있는 위치(모든 메서드)를 일컷는다.

- PointCut : Advice가 끼어드는(적용 ) 위치

- Aspect : PointCut + Advice 를 의미 =  Advicer

 

 

코드 예시)

 

1. Adivice에 바로 PointCut을 넣은 코드

@Slf4j
@Aspect
public class AspectV1 {
    @Around("execution(* hello.aop.order..*(..))")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}

 

 

2. Advice 와 PointCut 메서드로 분리 (포인트 컷의 재사용성을 높일 수 있다, 중복 사용 가능)

@Pointcut("execution(* hello.aop.order..*(..))")
    public void allOrder() {}

    @Pointcut("execution(* *..*Service.*(..))")
    public void allService() {}

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

    @Around("allOrder() && allService()")
    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());
        }
    }

 

3. 우선 순위 지정 예시

    우선 순위를 부여하기 위해서는 @Order 어노테이션을 사용 할 수 있다.

    클래스 단위로 적용됨으로 한 클래스에서 사용하려면 이너 클래스를 사용해야 함

    @Aspect
    @Order(2)
    public static class logAspect {
        @Around("hello.aop.order.aop.Pointcuts.allOrder()")
        public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[log] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
    }

    @Aspect
    @Order(1)
    public static class txAspect {
        @Around("hello.aop.order.aop.Pointcuts.allOrderAndService()")
        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());
            }
        }
    }

 

4. Advice를 적용하는 다양한 어노테이션

  • @Around
    • target의 메서드가 호출되는 앞 뒤에 추가적인 기능을 넣을 수 있다.
    • target의 메서드를 호출(.proceed())를 하지 않으면 예외가 발생한다.
    • 메서드를 호출하여 Object로 받기 때문에 결과값을 변경하는 등 행위를 할 수 있다.
  • @Before
    • 메서드가 실행되기 직전 실행할 부가기능을 정의할 수 있다.
    • @Around 와 다르게 메서드를 호출하지 않아도 예외가 일어나지 않는다.
  • @AfterReturning
    • 메서드가 실행된 후의 결과값이 나온 후 부가기능이 수행되도록 한다.
    • 어노테이션 안의 값으로 return 값을 받아올 수 있다.
    • return 값도 결과값도 컨트롤 할 수 있다.
  • @AfterThrowing
    • 메서드가 예외를 던졌을 때 실행하는 부가기능을 정의할 수 있다.
    • throwing 으로 받아 쓸 수있다.
  • @After
    • 가장 마지막 수행 되는 부가기능 로직
@Around("hello.aop.order.aop.Pointcuts.allOrderAndService()")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            //@Before
            log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
            Object result = joinPoint.proceed();
            //@AfterReturning
            log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            //@AfterThrowing
            log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
            throw e;
        } finally {
            //@After
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
        }
    }

    @Before("hello.aop.order.aop.Pointcuts.allOrderAndService()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("[before] {}", joinPoint.getSignature());
    }
    
    @AfterReturning(value="hello.aop.order.aop.Pointcuts.allOrderAndService()",returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        log.info("[return] {}", joinPoint.getSignature(),result);
    }
    
    @AfterThrowing(value="hello.aop.order.aop.Pointcuts.allOrderAndService()",throwing = "ex")
    public void doThrowing(JoinPoint joinPoint, Exception ex) {
        log.info("[ex] {}", joinPoint.getSignature(),ex);
    }
    
    @After("hello.aop.order.aop.Pointcuts.allOrderAndService()")
    public void doAfter(JoinPoint joinPoint) {
        log.info("[after] {}", joinPoint.getSignature());
    }

 

코드 출처

인프런 / 김영한 / 스프링 핵심원리, 고급편 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8