여기까지 하고 나면 남은건 그냥 문법 공부라고 해도 된다.

스프링 어드바이스 애너테이션은 종류가 몇가지 더 있음.

  1. @Around
  2. @Before
  3. @AfterReturning
  4. @After Throwing
  5. @After

하나씩 만들어보자.

package hello.aop.order.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Slf4j
@Aspect
public class AspectV6Advise {
    
    @Around("hello.aop.order.aop.Pointcuts.allOrderAndService()")
    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());
        }
    }
    
    //ProceedingJoinPoint 객체는 오직 @Around에서만 사용가능하다.
    //만약 @Before를 빈으로 등록했다면, 메서드의 실행 여부는 AOP에서는 컨트롤이 안됨. 그냥 자동으로 실행된다.
    @Before("hello.aop.order.aop.Pointcuts.allOrderAndService()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        log.info("[before] {}", joinPoint.getSignature());
    }
}

@Before 부터 하나씩 해볼건데, 이 친구들이 @Around 와 어떤차별점을 가지는가. 이게 왜 필요한가는 이걸 생각하면 된다.

⚠️ 왜 @Around 말고도 다른 어드바이스가 있을까?

@Around 를 사용하면 개발자가 메서드의 실행 여부까지 컨트롤 할 수 있다. 그래서 ProceedingJoinPoint 객체에서 proceed()직접 호출해줘야 함. 호출 안하면 체인이 끊긴다. (까먹기 쉽다.)

이렇게 까지 신경쓰기 싫다 그러면 이제 간단한 어드바이스들을 사용하면 된다. 이 친구들은 특정 시점에 프록시로 호출되기만 하고 타겟 메서드의 실행 흐름 제어까지는 하지 않기 때문에 간단하게 쓰기 좋음. 그 렇게 처리되기 위해서 @Around 와는 다르게 procedingJoinPoint 객체가 아니라 그냥 JoinPoint 객체가 주입된다.

물론 그것조차 안받아도 된다.

그리고 이 친구들은 AOP로직의 의도가 애너테이션 이름에 잘 묻어난다. 즉 가독성이 좋다.