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:06

추가로 트랜잭션을 적용하는 코드도 추가해 보자. 기능이 동작한 것 처럼 로그만 남겨보자.

트랜잭션 기능은 보통 다음과 같이 동작한다.

  • 핵심 로직 실행 직전에 트랜잭션을 시작
  • 핵심 로직 실행
  • 핵심 로직 실행에 문제가 없으면 커밋
  • 핵심 로직 실행에 예외가 발생하면 롤백

 

1. AspectV3


@Slf4j
@Aspect
public class AspectV3 {

    //hello.aop.order 패키지와 하위 패키지
    @Pointcut("execution(* hello.aop.order..*(..))")
    private void allOrder(){ } // pointcut signature

    //클래스 이름 패턴이 *Service
    @Pointcut("execution(* *..*Service.*(..))")
    private void allService(){}

    @Around("allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable{
        log.info("[[log] {}", joinPoint.getSignature());
        return joinPoint.proceed(); //target호출
    }

    //hello.aop.order 패키지와 하위 패키지이면서, 클래스 이름 패턴이 *Service
    @Around("allOrder() && allService()")
    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());
        }
    }
}
  • allOrder() 포인트컷은 hello.aop.order 패키지와 하위 패키지를 대상으로 한다.
  • allService() 포인트컷은 타입 이름 패턴이 *Service 를 대상으로 하는데 쉽게 이야기해서
    XxxService 처럼 Service 로 끝나는 것을 대상으로 한다. 
    *Servi* 과 같은 패턴도 가능하다.
  • 여기서 타입 이름 패턴이라고 한 이유는 클래스, 인터페이스에 모두 적용되기 때문이다.

 

  • @Around("allOrder() && allService()")
  • 포인트컷은 이렇게 조합할 수 있다. 
  • && (AND), || (OR), ! (NOT) 3가지 조합이 가능하다.
  • hello.aop.order 패키지와 하위 패키지 이면서 타입 이름 패턴이 *Service 인 것을 대상으로 한다.
  • 결과적으로 doTransaction() 어드바이스는 OrderService 에만 적용된다.
  • doLog() 어드바이스는 OrderService , OrderRepository 에 모두 적용된다.
  • 포인트것이 적용된 AOP
    orderService : doLog() , doTransaction() 어드바이스 적용
    orderRepository : doLog() 어드바이스 적용

 

2. 실행


@Slf4j
@SpringBootTest
//@Import(AspectV1.class) //스프링 빈으로 등록
//@Import(AspectV2.class) //스프링 빈으로 등록
@Import(AspectV3.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);
    }

}
  • success() 결과
[[log] void hello.aop.order.OrderService.orderItem(String)
[트랜잭션 시작] 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)

  • exception() 결과
    rollback 호출
[[log] void hello.aop.order.OrderService.orderItem(String)
[트랜잭션 시작] 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. AOP 적용 전 후 


  • AOP 적용 전
    클라이언트 --> orderService.orderItem() --> orderRepository.save()
  • AOP 적용 후
    클라이언트 --> [ doLog() --> doTransaction() ] --> orderService.orderItem() --> [ doLog() ] --> orderRepository.save()
  • orderService 에는 doLog() , doTransaction() 두가지 어드바이스가 적용되어 있고,
    orderRepository 에는 doLog() 하나의 어드바이스만 적용된 것을 확인할 수 있다.

 

4. ?????????


  • 어드바이스가 적용되는 순서를 변경하고 싶으면???????
  • 예를 들어서 실행 시간을 측정해야 하는데 트랜잭션과 관련된 시간을 제외하고 측정하고 싶다?
  • [ doTransaction() doLog() ] 이렇게 트랜잭션 이후에 로그를 남겨야 할 것이다.

 

5. GitHub :220105 SpringAOP - multiAdvice


 

GitHub - bsh6463/SpringAOP

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

github.com

 

Comments