- 가장 기본적인 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을 사용하여 직접 등록
- @Component를 사용하여 컴포넌트 스캔으로 자동 등록
- @Import를 사용, 주로 설정 파일을 추가할 때 사용(@Configuration)
- 포인트컷 분리
@Aspect
@Slf4j
public class AspectV2 {
@Pointcut("execution(* practice.aop.order..*(..))")
private void allOrder() {}
@Around("allOrder()")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
- @Pointcut 안에 포인트컷 표현식으로 값을 넣음
- 메소드 이름과 파라미터를 합쳐서 '포인트컷 시그니처(Signature)'라고 함, 위 예제에서는 allOrder()가 포인트컷 시그니처
- 코드 내용은 비워둠
- @Around 안에 포인트컷 시그니처를 넣어서 사용 가능
- 다른 Aspect에서 해당 Aspect를 참고하려면 private를 public으로 변경할 것
- 어드바이스 추가
@Aspect
@Slf4j
public class AspectV3 {
@Pointcut("execution(* practice.aop.order..*(..))")
private void allOrder() {}
@Pointcut("execution(* *..*Service.*(..))")
private 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());
}
}
}
@Around("allOrder() && allService()")
와 같이 포인트컷을 조합할 수 있음,
조합 시에 &&(AND), ||(OR), !(NOT)
을 사용
- 포인트컷 참조
@Slf4j
@Aspect
public class Pointcuts {
@Pointcut("execution(* practice.aop.order..*(..))")
public void allOrder() {}
@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으로 지정 해야 함
- 포인트컷을 여러 어드바이스에서 함께 사용할 때 해당 방법을 사용하면 효과적
- 어드바이스 순서
@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()")
public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
}
}
- 어드바이스의 순서를 지정하려면 @Aspect 적용 단위로 @Order를 적용해야 함
- 다만, @Order는 어드바이스 단위가 아닌 클래스 단위로만 적용이 가능 함
-> 즉, Aspect를 별도의 클래스로 분리 해야 함