오늘은 AOP의 구현 파트를 수강 했다.
@Aspect와 관련해서 여러가지를 배울 수 있는 강의였다.
@Aspect가 붙은 클래스 안에는 포인트 컷과 어드바이스가 존재하고
이것을 알아서 어드바이저로 만들어서 AOP 기능을 수행해준다.
우선 순차적으로 정리해보자!
@Slf4j
@Aspect
public class AspectV1 {
//hello.aop.order 패키지와 하위 패키지
@Around("execution(* hello.aop.order..*(..))")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature()); //join point 시그니처
return joinPoint.proceed();
}
}
@Around
가 포인트컷이 되고 어떤 녀석들에 적용할 지 지정해준다. 표현식은 AspectJ의 표현식을 사용.
doLog()
가 어드바이스가 된다. 어떤 동작을 수행할 지 지정.
@Around
는 반드시 joinpoint.proceed()
를 수행해야 한다. 뒤에 더 자세히 설명하겠다.
@Slf4j
@Aspect
public class AspectV2 {
//포인트컷 분리
//hello.aop.order 패키지와 하위 패키지
@Pointcut("execution(* hello.aop.order..*(..))")
private void allOrder() {} //pointcut signature
@Around("allOrder()")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature()); //join point 시그니처
return joinPoint.proceed();
}
}
@Pointcut
을 사용해서 분리 할 수 있다. 이때 publlic
으로 지정하면 다른 곳에서도 참조 할 수 있다. (이후 포인트컷 외부 참조에서 다룸)
@Around
에는 @Pointcut
의 메소드 이름을 적어주면 된다.
@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()); //join point 시그니처
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("[트랜잭션 롤백] {}", joinPoint.getSignature());
throw e;
} finally {
log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
}
}
}
@Pointcut
과 @Around
를 여러개 사용할 수도 있다.
이 코드에서부터는 트랜잭션의 동작을 간단하게 로그만 남겨서 알아보았다. 이 코드를 토대로 이후에 어드바이스의 여러 종류에 대해 알아볼 수 있다.
위의 코드에 있는 @Pointcut
을 따로 클래스를 만들어 놔두고 참조해서 사용해 보자!
//포인트컷 클래스
public class Pointcuts {
//hello.aop.order 패키지와 하위 패키지
@Pointcut("execution(* hello.aop.order..*(..))")
public void allOrder() {} //pointcut signature
//클래스 이름 패턴이 *Service
@Pointcut("execution(* *..*Service.*(..))")
public void allService() {}
//allOrder && allService
@Pointcut("allOrder() && allService()")
public void orderAndService() {}
}
//Aspect 클래스
@Slf4j
@Aspect
public class AspectV4Pointcut {
//포인트컷 참조
@Around("hello.aop.order.aop.Pointcuts.allOrder()")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature()); //join point 시그니처
return joinPoint.proceed();
}
//hello.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
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());
}
}
}
이렇게 외부 클래스의 포인트컷을 참조하기 위해서는 우선 @Pointcut
을 public
으로 만들어야 하고
Aspect 클래스에서는 @Around
에 패키지명.클래스명.메소드명()
으로 받아오면 된다.
하나의 Aspect 안에서는 순서를 보장하지 않는다.
그래서 따로 클래스를 분리해서 @Order
를 사용해서 순서를 지정할 수 있다.
따로 클래스를 만들어야 하지만 해당 예제에서는 static class
를 사용해서 했다.
@Slf4j
public class AspectV5Order {
//어드바이스 순서를 지정하기 위해 @Order 사용.
//@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()); //join point 시그니처
return joinPoint.proceed();
}
}
@Aspect
@Order(1) //이게 먼저 실행된다.
public static class TxAspect {
//hello.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service
@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
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());
}
}
}
}
우선 간단하게 여기까지만 정리하고 마지막 남은 어드바이스의 여러 종류는 따로 정리하도록 하겠다.