스프링/스프링 AOP

@Pointcut - 포인트컷 분리

chanhee01 2023. 9. 22. 14:36

AOP 동작을 위한 service, repository 코드

 

OrderRepository.class

@Slf4j
@Repository
public class OrderRepository {
    public String save(String itemId) {
        log.info("[orderRepository] 실행");
        //저장 로직
        if (itemId.equals("ex")) {
            throw new IllegalStateException("예외 발생!");
        }
        return "ok";
    }
}

 

 

OrderService.class

@Slf4j
@Service
public class OrderService {
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void orderItem(String itemId) {
        log.info("[orderService] 실행");
        orderRepository.save(itemId);
    }
}

 

AOP가 적용되는지 확인을 위한 Service와 Repository 코드이다. 진짜 간단하게 로그를 통해 어떤 것이 실행되는지만 적어놨다.

 

 

포인트컷 분리 전 - @Around에 경로

@Slf4j
@Aspect
public class AspectV1 {

    @Around("execution(* hello.aop.order..*(..))") // 포인트컷
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); // joinPoint 시그니처 (메서드 뭐 호출되었는지)
        return joinPoint.proceed();
    }
}

이전에도 포스팅했지만 @Aspect 어노테이션을 활용하면 aop를 쉽게 적용할 수 있다.

 

 

 

포인트컷 분리 전 - @Poincut 사용

@Slf4j
@Aspect
public class AspectV2 {
    
    // hello.aop.order 패키지와 하위 패키지
    @Pointcut("execution(* hello.aop.order..*(..))") // 포인트컷
    private void allOrder(){}

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

@Around에 포인트컷의 경로를 넣는 것이 아니라 @Pointcut 어노테이션을 이용해서 거기에 경로를 설정할 수도 있다. @Around 어노테이션에는 allOrder()라는 메서드를 넣어주면 된다.

 

 

allOrder()의 코드 내용은 비워두고 반환 타입은 무조건 void여야 한다.

 

이렇게 하면 doLog2() 등 다른 aop가 있을 때 allOrder()를 넣어줘서 같은 포인트 컷을 넣어줄 수 있다.

 

물론 @Around와 @Poincut 어디에 경로를 넣든 결과는 똑같다.

 

하나의 포인트컷 표현식으로 여러 어드바이스에서 함께 사용할 수도 있으며 public으로 선언하면 다른 클래스에 있는 외부 어드바이스에서도 포인트컷을 함께 사용할 수도 있다.

 

 

 

Pointcut.class

package hello.aop.order.aop;

import org.aspectj.lang.annotation.Pointcut;

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

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

    // 위 2개의 동시 조건 포인트컷
    @Pointcut("allOrder() && allService()")
    public void orderAndService(){}
}

포인트컷 클래스를 만들고 그 클래스의 경로를 입력하면 포인트컷을 외부에서 사용할 수 있다.

@Pointcut("hello.aop.order.aop.Pointcuts.allOrder()")
private void allOrder(){}

포인트컷의 경로.클래스이름.메서드 이름을 통해 포인트컷의 경로를 넣어줬다.