
어드바이스는 어느 시점에 어떤 기능을 의미한다.
따라서, 이번 포스팅에서는 어드바이스의 시점 지정에 대해 알아볼 것이다.
어드 바이스는 다양한 어노테이션을 AOP 실행 시점을 지정할 수 있다.
| 어노테이션 | 설명 |
|---|---|
| @Around | - 메서드 호출 전후에 수행 |
| - 가장 강력한 어드바이스 | |
| - 조인 포인트 실행 여부 선택 | |
| - 반환 값 변환 | |
| - 예외 변환 등이 가능 | |
| @Before | - 조인 포인트 실행 이전에 실행 |
| @After | - 조인 포인트가 정상 또는 예외에 관계없이 실행 (finally와 유사) |
| @AfterReturning | - 조인 포인트가 정상 완료 후 실행 |
| @AfterThrowing | - 메서드가 예외를 던지는 경우 실행 |
@Aspect
@Order(2)
public static class TxAspect {
@Around("isyoudwn.aop.order.aop.PointCuts.orderAndService()")
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());
}
}
}
💡 Around 하나만 있어도, AOP의 모든 시점을 구현할 수 있다.
그러나, Target을 직접 실행해야 되는 단점이 있고, Advice의 내부 로직을 이해해야지만, 그 Advice가 어떤 시점에 실행되는지 알 수 있다.
따라서, 아래와 같이 시점을 특정하는 어노테이션이 존재한다.
조인 포인트 실행 전 Advice 실행
// before은 로직 수행 후 joinpoint를 실행한다
@Before("isyoudwn.aop.order.aop.PointCuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}

메서드 실행 후 정상적으로 Return될 때 Advice 실행
@AfterReturning(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
log.info("[return] {} return = {}", joinPoint.getSignature(), result);
}
returning속성에 사용된 이름과 파라미터를 동일한 이름으로 지정하면, 매칭되어서 파라미터에 return 값이 들어온다. 이때, 파라미터 타입은 메서드의 반환 타입과 동일해야 된다(부모 타입을 지정하면 모든 자식 타입은 인정된다)메서드 실행이 예외를 던져서 종료될 때 실행될 때 Advice 실행
@AfterThrowing(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()", throwing = "ex")
public void doReturn(JoinPoint joinPoint, Exception ex) {
log.info("[ex] message = {}", ex.getMessage());
}
throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야한다throwing 파라미터에 지정된 타입과 맞는 예외를 대상으로 실행한다(부모 타입을 지정하면, 모든 자식 타입은 인정된다)메서드 실행이 종료되면 Advice가 실행된다. (finally를 생각하면 된다)
@After(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()")
public void doAfter(JoinPoint joinPoint) {
log.info("[after] {}", joinPoint.getSignature());
}
@Slf4j
@Aspect
public class AspectV6Advice {
// before은 로직 수행 후 joinpoint를 실행한다
@Before("isyoudwn.aop.order.aop.PointCuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}
@AfterReturning(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
log.info("[return] {} return = {}", joinPoint.getSignature(), result);
}
@AfterThrowing(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()", throwing = "ex")
public void doReturn(JoinPoint joinPoint, Exception ex) {
log.info("[ex] message = {}", ex.getMessage());
}
@After(value = "isyoudwn.aop.order.aop.PointCuts.orderAndService()")
public void doAfter(JoinPoint joinPoint) {
log.info("[after] {}", joinPoint.getSignature());
}
}
Around를 사용하지 않고, 시점을 특정하는 어노테이션을 총동원해서 작성한 Advice 들의 실행 결과는 아래와 같다.



Aspect 안에 동일한 종류의 어드바이스가 2개이면 순서가 보장되지 않기 때문에, 이 경우에는 @Order을 사용해서 순서를 지정해주어야 한다!