스프링 핵심 원리(고급편)

Ch10. 스프링 AOP(구현) - 스프링 AOP 구현(어드바이스 추가)

webmaster 2022. 4. 19. 19:28
728x90
@Slf4j
@Aspect
public class AspectV3 {

    //hello.aop.order 패키지와 하위 패키지
    @Pointcut("execution(* hello.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());//joinPoint 시그니처
        return joinPoint.proceed();
    }

    //hello.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
    @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("[log] {} ", joinPoint.getSignature()); //join point 시그니처
            throw e;
        }finally {
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
        }
    }
}
  • allOrder() 포인트 컷은 hello.aop.order 패키지와 하위 패키지를 대상으로 한다.
  • allService() 포인트컷은 타입 이름 패턴이 *Service를 대상으로 하는데 쉽게 이야기해서 XxxService처럼 Service로 끝나는 것을 대상으로 한다. *Servi* 과 같은 패턴도 가능하다.
  • 여기서 타입 이름 패턴이라고 한 이유는 클래스, 인터페이스에 모두 적용되기 때문이다.
  • @Around("allOrder() && allService()")
    • 포인트컷은 이렇게 조합할 수 있다.
    • && (AND)
    • || (OR)
    • ! (NOT)
  • hello.aop.order 패키지와 하위 패키지 이면서 타입 이름 패턴이 *Service 인 것을 대상으로 한다
  • 결과적으로 doTransaction() 어드바이스는 OrderService 에만 적용된다.
  • doLog() 어드바이스는 OrderService , OrderRepository에 모두 적용된다.

실행

@Slf4j
@SpringBootTest
@Import(AspectV3.class)
public class AopTest {
	...
}

적용 그림

orderService 에는 doLog() , doTransaction() 두가지 어드바이스가 적용되어 있고, orderRepository 에는 doLog() 하나의 어드바이스만 적용된 것을 확인할 수 있다

로그 남기는 순서가 doLog -> doTx 순서로 동작한다. 만약 어드바이스가 적용되는 순서를 변경하고 싶으면 어떻게 하면 될까? 예를 들어서 실행 시간을 측정해야 하는데 트랜잭션과 관련된 시간을 제외하고 측정하고 싶다면 [ doTransaction() doLog() ] 이렇게 트랜잭션 이후에 로그를 남겨야 할 것이다. 

728x90