Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

[스프링AOP] 스프링AOP 구현 - Advice 순서 본문

인프런/[인프런] 스프링 핵심 원리 - 고급

[스프링AOP] 스프링AOP 구현 - Advice 순서

Seung__ 2022. 1. 5. 16:27

어드바이스는 기본적으로 순서를 보장하지 않는다. 

순서를 지정하고 싶으면 @Aspect 적용 단위로 org.springframework.core.annotation.@Order 애노테이션을 적용필요.

어드바이스 단위가 아니라 클래스 단위로 적용할 수 있다는 점이다. 

그래서 지금처럼 하나의 애스펙트에 여러 어드바이스가 있으면 순서를 보장 받을 수 없다.

 따라서 애스펙트를 별도의 클래스로 분리해야 한다.

 

로그를 남기는 순서를 바꾸어서 [ doTransaction() doLog() ] 트랜잭션이 먼저 처리되고, 

이후에 로그가 남도록 변경해보자.

 

 

1. AspectV5


@Slf4j
public class AspectV5Order {

    @Aspect
    @Order(2)
    public static class LogAspect{
        @Around("hello.aop.order.aop.Pointcuts.allOrder()")
        public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable{
            log.info("[[log] {}", joinPoint.getSignature());
            return joinPoint.proceed(); //target호출
        }
    }

    @Aspect
    @Order(1)
    public static class TransactionAspect{
        //hello.aop.order 패키지와 하위 패키지이면서, 클래스 이름 패턴이 *Service
        @Around("hello.aop.order.aop.Pointcuts.orderAndService()")
        public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable{
            try{
                log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
                Object result = joinPoint.proceed();
                log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
                return result;
            }catch (Exception e){
                log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
                throw e;
            }finally {
                log.info("[리로스 릴리즈] {}", joinPoint.getSignature());
            }
        }
    }


}
  • 하나의 애스펙트 안에 있던 어드바이스를 LogAspect , TxAspect 애스펙트로 각각 분리했다. 
  • 한 곳에서 관리하기 위해 내부 클래스로 적용함.
  • 그리고 각 애스펙트에 @Order 애노테이션을 통해 실행 순서를 적용했다.->적용되는 위치 지정.
  •  참고로 숫자가 작을 수록 먼저 실행된다.

 

 

2. 실행


@Slf4j
@SpringBootTest
//@Import(AspectV1.class) //스프링 빈으로 등록
//@Import(AspectV2.class) //스프링 빈으로 등록
//@Import(AspectV3.class) //스프링 빈으로 등록
//@Import(AspectV4Pointcut.class) //스프링 빈으로 등록
@Import({AspectV5Order.LogAspect.class, AspectV5Order.TransactionAspect.class}) //스프링 빈으로 등록
public class AopTest {

    @Autowired
    OrderService orderService;

    @Autowired
    OrderRepository orderRepository;

    @Test
    void aopInfo(){
        log.info("isAopProxy, orderService={}", AopUtils.isAopProxy(orderService));
        log.info("isAopProxy, orderRepository={}", AopUtils.isAopProxy(orderRepository));
    }

    @Test
    void success(){
        orderService.orderItem("itemA");
    }

    @Test
    void exception(){
        assertThatThrownBy(() -> orderService.orderItem("ex")).isInstanceOf(IllegalStateException.class);
    }

}
  • 실행 결과를 보면 트랜잭션 어드바이스가 먼저 실행되는 것을 확인할 수 있다.
[트랜잭션 시작] void hello.aop.order.OrderService.orderItem(String)
[[log] void hello.aop.order.OrderService.orderItem(String)
[orderService] 실행
[[log] String hello.aop.order.OrderRepository.save(String)
[orderRepository] 실행
[트랜잭션 커밋] void hello.aop.order.OrderService.orderItem(String)
[리로스 릴리즈] void hello.aop.order.OrderService.orderItem(String)

 

 

3. GitHub: 220105 SpringAop - Advice 순서, Apect 분리


 

GitHub - bsh6463/SpringAOP

Contribute to bsh6463/SpringAOP development by creating an account on GitHub.

github.com

 

Comments