Spring AOP - Advice 종류

목차

Spring 핵심원리 고급편 - 어드바이스 종류

종류 설명
@Around 메서드 호출 전 후에 실행, 가장 강력한 어드바이스, 조인 포인트 실행 여부 선택, 반환 값 변환, 예외 변환 등이 가능
@Before 조인 포인트 실행 이전에 실행
@AfterReturning 조인 포인트가 정상 완료 후 실행
@AftThrowing 메서드가 예외를 던지는 경우 실행
@After 조인 포인트가 정상 또는 예외에 관계 없이 실행
@Slf4j
@Aspect
public class AspectV6Advice {

// com.example.aop.order 하위 패키지면서 클래스 이름 패턴이 *Service 인 것
@Around("com.example.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 ex){
// @AfterThrowing
log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
throw ex;
}finally {
// @After
log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
}
}
}
@Slf4j
@Aspect
public class AspectV6Advice {

// com.example.aop.order 하위 패키지면서 클래스 이름 패턴이 *Service 인 것
@Around("com.example.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 ex){
// @AfterThrowing
log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
throw ex;
}finally {
// @After
log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
}
}

// @Before 은 그냥 실행해준다.
@Before("com.example.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint){
log.info("[before] {}", joinPoint.getSignature());
}

// return 값을 조작을 할 수는 있지만 바꿀 수 는 없다.
@AfterReturning(value = "com.example.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result){
log.info("[return] {} return = {}", joinPoint.getSignature(), result);
}

// 자동으로 throw ex 를 해준다.
@AfterThrowing(value = "com.example.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
public void doThrowing(JoinPoint joinPoint, Exception ex){
log.info("[ex] {} message = {}", ex);
}

@After(value = "com.example.aop.order.aop.Pointcuts.orderAndService()")
public void doAfter(JoinPoint joinPoint){
log.info("[after] {}", joinPoint.getSignature());
}
}

모든 어드바이스는 JoinPoint 를 첫번째 파라미터에 사용할 수 있다. 단 @Around 는 ProceedingJoinPoint 를 사용해야 한다.
ProceedingJoinPoint는 JoinPoint 의 하위 타입니다.

JoinPoint 인터페이스의 주요 기능

Method 설명
getArgs 메서드 인수를 반환한다.
getThis 프록시 객체를 반환한다.
getTarget 대상 객체를 반환한다.
getSignature 조언되는 메서드에 대한 설명을 반환한다.
toString 조언되는 방법에 대한 유용한 설명을 인쇄한다.

ProceedingJoinPoint 인터페이스의 주요 기능

  • proceed
    • 다음 어드바이스나 타겟을 호출한다.

어드바이스 종류

@Before

조인포인트 실행 전에 작동한다.

  • @Around 와는 다르게 작업 흐름을 변경할 수 없다.
  • @Around 는 ProceedingJoinPoint.proceed 를 호출해야 다음 대상이 호출된다.
  • 만약 호출하지 않는다면 다음 대상이 호출 되지 않는다.
  • @Before 는 ProceedingJoinPoint.proceed 메소드를 사용하지 않는다.
  • 메서드 종료시 자동으로 다음 타켓이 호출된다.

@AfterReturning

메서드 실행이 정상적으로 반화될 때 실행한다.

  • returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
  • returnning 절에 지정된 타입의 값을 반환하는 메서드만 대상으로 실행한다.
  • @Around 와 다르게 반환되는 객체를 변경할 수 없다.
  • 반환 객체를 조작할 수는 있다.

@AfterThrowing

메서드 실행이 예외를 던져서 종료될 때 실행한다.

  • throwing 속성에 사용된 이름은 어드바이스 메서드의 매게변수 이름과 일치해야 한다.
  • throwing 절에 지정된 타입과 맞은 예외를 대상으로 실행한다.
  • 부모 타입을 지정하면 모든 자식 타입은 인정된다.

@After

메서드 실행이 종료되면 실행된다.

  • 정상 및 예외 반환 조건을 모두 처리한다.
  • 일반적으로 리소스 및 유사한 목적을 해제하는데 사용한다.

@Around

메서드의 실행의 주변에서 실행된다. 메서드 실행 전후에 작업을 수행한다.

  • 가장 강력한 어드바이스
    • 조인 포인트 실행 여부 선택
    • 전달 값 변환
    • 반환 값 변환
    • 예외 변환
    • 트랜잭션 처럼 try ~ catch ~ finally 모두 들어가는 구문 처리 가능
  • 어드바이스의 첫 번째 파라미터는 ProceedingJoinPoint 를 사용해야 한다.
  • proceed 메서드를 통해 대상을 실행한다.
  • proceed 메서드를 여러번 실행할 수도 있다.

@Around 외 다른 어드바이스가 존재하는 이유

  • @Around 는 항상 joinPoint.proceed 메소드를 호출해야 한다.
  • 만약, 실수로 호출하지 않는다면 타겟이 호출되지 않는 치명적인 버그가 발생한다.
@Slf4j
@Aspect
public class AspectV6Advice {

// com.example.aop.order 하위 패키지면서 클래스 이름 패턴이 *Service 인 것
@Around("com.example.aop.order.aop.Pointcuts.orderAndService()")
public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable{
try{
// @Before
log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
Object result = joinPoint.proceed();
  • @Before 는 joinPoint.proceed 메소드를 호출하는 고민을 하지 않아도 된다.
// @Before 은 그냥 실행해준다.
@Before("com.example.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint){
log.info("[before] {}", joinPoint.getSignature());
}
Share