Tech/Spring

스프링 AOP - 어드바이스 종류

봄의 개발자 2023. 10. 20.
728x90
반응형

Advice 종류

  • @Around
    • 메서드 호출 전후에 수행
    • 가장 강력한 어드바이스
    • join point 실행 여부 선택
    • 반환 값 변환
    • 예외 변환 등 가능
    → 다 할 수 있음
  • @Before
    • join point 실행 이전에 실행
  • @AfterReturning
    • join point가 정상 완료 후 실행
  • @AfterThrowing
    • 메서드가 예외를 던지는 경우 실행
  • @After
    • join point가 정상 또는 예외에 관계 없이 실행 (finally)
@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("[before] {}", joinPoint.getSignature());
    }
    
    @AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
    public void doReturn (JoinPoint joinPoint, Object result) {
        log.info("[return] {} return={}", joinPoint.getSignature(), result);
    }
    
    @AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
    public void doThrow(JoinPoint joinPoint, Exception ex) {
        log.info("[ex] {} message={}", joinPoint.getSignature(), ex); // 자동으로 throw e 해줌
    }

    @After(value = "hello.aop.order.aop.Pointcuts.orderAndService()")
    public void doAfter(JoinPoint joinPoint) {
        log.info("[after] {}", joinPoint.getSignature());
    }
  • @Around 어드바이스만 사용해도 필요한 기능 모두 수행 가능

참고 정보 획득

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

ProceedingJoinPoint 인터페이스 주요 기능

  • proceed(): 다음 어드바이스나 타켓 호출
  • Around는 다음을 내가 직접 호출해줘야함

JoinPoint 인터페이스 주요 기능

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

@Before

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

@AfterReturning

  • returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 함
  • returning 절에 지정된 타입의 값을 반환하는 메서드만 대상으로 실행함
    • 부모 타입을 지정하면 모든 자식 타입이 인정된다.
    • 반환값이 void일 경우에 매개변수 타입을 String으로 둔다면 값을 받아오지 못해 실행하지 않는다.
  • @Around와 다르게 반환되는 객체를 변경할 수는 없다.
    • 반환 객체를 조작할 수는 있다 → 예를 들어 setter 호출
  • 반환 객체를 변경하려면 @Around를 사용해야함

@AfterThrowing

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

@After

  • 메서드 실행 종료되면 실행됨 (finally)
  • 정상 및 예외 반환 조건 모두 처리
  • 일반적으로 리소스 해제할 때 사용

@Around

  • 메서드 실행의 주변에서 실행
  • 메서드 실행 전후에 작업 수행
  • 가장 강력한 어드바이스
    • 조인 포인트 실행 여부 선택: joinpoint.proceed() 호출 여부 선택
    • 전달값 변환: joinPoint.proceed(args[])
    • 반환 값 변환
    • 예외 변환
    • 트랜잭션처럼 try~catch~finally 모두 들어가는 구문 처리 가능
  • 어드바이스 첫 번째 파라미터 ProceedingJoinPoint사용
  • proceed()를 통해 대상을 실행
  • proceed() 여러번 실행할 수 있음 (재시도)

  • 스프링은 5.2.7 버전부터 동일한 @Aspect 안에서 동일한 조인포인트의 우선순위를 정했다.
  • 실행 순서: @Around , @Before , @After , @AfterReturning , @AfterThrowing

어드바이스가 적용되는 순서는 이렇게 적용되지만, 호출 순서와 리턴 순서는 반대라는 점을 알아두자.

물론 @Aspect 안에 동일한 종류의 어드바이스가 2개 있으면 순서가 보장되지 않는다. 이 경우 앞서 배운 것 처럼 @Aspect 를 분리하고 @Order 를 적용하자.


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

@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(ProceedingJoinPoint joinPoint) {
	log.info("[before] {}", joinPoint.getSignature());
}
  • 문제점: 타겟을 호출하지 않는다. 부가 기능만 수행하고 끝내버린다
  • @Around는 항상 joinPoint.proceed()를 호출해야한다.
    • 만약 실수로 호출하지 않으면 치명적인 버그가 발생한다.
  • @Before 는 joinPoint.proceed() 를 호출하는 고민을 하지 않아도 된다.
  • @Around가 가장 넓은 기능을 제공하는 것은 맞지만 실수할 가능성이 있다.
  • 반면에 @Before, @After 같은 어드바이스는 기능은 적지만 실수할 가능성이 낮고 코드도 단순하다.
  • 이 코드를 작성한 의도가 명확하게 드러난다는 점이 중요하다!

좋은 설계는 제약이 있는 것이다!

  • 제약이 있어야 역할이 명확해진다.
  • 코드의 의도를 파악하기 쉬워진다.
728x90
반응형

댓글