ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 어드바이스 종류
    스프링/스프링 AOP 2023. 9. 22. 20:55
    1. @Around : 메서드 호출 전후에 수행, 조인 포인트 실행 여부 선택, 반환 값 변환 ,예외 변환 등 가능
    2. @Before : 조인 포인트 실행 이전에 실행
    3. @AfterReturning : 조인 포인트가 정상 완료후 실행
    4. @AfterThrowing : 메서드가 예외를 던지는 경우 실행
    5. @After : 조인 포인트가 정상 또는 예외에 관계없이 실행(finally)

     

     

    2. @Before

    @Before("hello.aop.order.aop.Pointcuts.orderAndService()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("[before] {}", joinPoint.getSignature());
    }

    @Before 어노테이션은 조인 포인트 실행 이전에 실행이 된다. Service 계층에만 조인 포인트를 걸었으니, 로직을 실행하게 된다면 hello가 출력되고 orderService -> orderRepository 순으로 실행될 것이다.

     

    여기서 주목해야 할 점은 이전의 엄청 긴 로직에 비해 한줄만 로직에 들어간다는 것이다. 포인트 컷을 걸어두었던 로직이 실행되기 전에 실행되는 것이기 때문에 proceed()를 하거나 다른 로직이 포함되지 않아도 된다.

     

    @Around는 ProceedingJoinPoint.proceed()를 호출해야만 다음 대상이 호출되는 반면 @Before는 ProceedingJoinPoint.proceed() 자체를 사용하지 않고, 예외가 발생하지 않는 한 메서드 종료시 자동으로 다음 타겟이 호출된다.

     

     

     

    3. @AfterReturning

    @AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
    public void doReturn(JoinPoint joinPoint, Object result) {
        log.info("[return] {} return={}", joinPoint.getSignature(), result);
    }

    정상 실행되었을 때 result를 return 해주는 것이다. returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야한다. 매개변수의 Object result에서 반환타입인 Object는 되도록 Object로 하는것이 좋다. 왜냐하면 메서드의 반환 타입과 같아야하는데, Object는 모두 반환할 수 있기 때문이다.

     

     

    4. @AfterThrowing

    @AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
    public void doThrowing(JoinPoint joinPoint, Exception ex) {
        log.info("[ex] {} message={}", ex);
    }

    예외가 발생했을 시 예외를 throw 해준다. 예외도 마찬가지로 부모 타입을 지정하면 모든 자식 타입은 인정되므로 Exceptino을 반환해주는 것이 좋다.

     

     

    5. @After

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

    try - catch 문의 finally 같은 역할을 한다. 정상 및 예외 반환 조건을 모두 처리한다.

     

     

     

     

    크게 어렵거나 복잡한 내용이 없다. 왜냐하면 @Around에서 이미 다 했던 역할이기 때문이다.

    @Around("hello.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());
            return e;
        } finally {
            // @After
            log.info("[리소스 릴리스] {}", joinPoint.getSignature());
        }
    }

    각 주석이 있는 곳에서 역할을 수행하는 것뿐이다.

     

    사실 @Around 안에서 모든 것들을 다 해결할 수 있기 때문에 거의 대부분 @Around만 사용해도 모든 기능을 구현할 수 있다.

     

    사진 : 인프런 스프링 강의

    동일한 @Aspect 안에 1번부터 5번까지 실행된다면 위와 같은 실행 순서를 가지게 된다.

    어드바이스가 적용되는 순서는 위와 같지만 호출 순서와 리턴 순서는 반대이다.

     

     

     

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

    @Around("hello.aop.order.aop.Pointcuts.orderAndService()")
    public void doBefore(ProceedingJoinPoint joinPoint) {
        log.info("[before] {}", joinPoint.getSignature());
    }

    @Around에서는 proceed를 실수로라도 호출하지 않으면 다음 대상이 호출되지 않는다. 하지만 @Before은 proceed를 호출하지 않아도 자동으로 다음 대상이 호출된다. 실수라 하더라도 매우 치명적인 실수가 된다.

     

    또한 @Before, @After와 같은 어드바이스는 기능이 적긴 하지만 코드의 의도를 명확하게 파악할 수 있다는 장점이 있다.

     

     

     

    그럼에도 불구하고 proceed를 잘 호출해서 @Around만 사용하는 것이 좋지 않을까 하는 개인적인 생각이다......

    '스프링 > 스프링 AOP' 카테고리의 다른 글

    어노테이션으로 AOP 사용  (0) 2023.09.23
    포인트컷 - execution  (0) 2023.09.23
    어드바이스 순서  (0) 2023.09.22
    @Pointcut - 포인트컷 분리  (0) 2023.09.22
    @Aspect 프록시  (0) 2023.09.20
Designed by Tistory.