Spring/AOP

์Šคํ”„๋ง AOP ๊ตฌํ˜„

an_jjin 2024. 11. 13. 22:55
๐Ÿ’ก ๊น€์˜ํ•œ๋‹˜์˜ ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ ๊ณ ๊ธ‰ํŽธ ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

@Aspect ์‚ฌ์šฉํ•˜์—ฌ AOP ๊ตฌํ˜„

@Slf4j
@Service
public class OrderService {

    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void orderItem(String itemId) {
        log.info("[orderService] ์‹คํ–‰");
        orderRepository.save(itemId);
    }
}
@Slf4j
@Repository
public class OrderRepository {

    public String save(String itemId) {
        log.info("[orderRepository] ์‹คํ–‰");
        //์ €์žฅ ๋กœ์ง
        if (itemId.equals("ex")) {
            throw new IllegalStateException("์˜ˆ์™ธ ๋ฐœ์ƒ!");
        }
        return "ok";
    }
}
@Slf4j
@Aspect
public class AspectV1 {

    //hello.aop.order ํŒจํ‚ค์ง€์™€ ํ•˜์œ„ ํŒจํ‚ค์ง€
    @Around("execution(* hello.aop.order..*(..))")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); //join point ์‹œ๊ทธ๋‹ˆ์ฒ˜
        return joinPoint.proceed();
    }
}
@Slf4j
@SpringBootTest
@Import(AspectV1.class)
public class AopTest {

    @Autowired
    OrderService orderService;

    @Autowired
    OrderRepository orderRepository;

    ...

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

    ...

}

joinPoint.proceed()

joinPoint.proceed()์˜ ๋ฆฌํ„ด์€ ์ฃผ๋กœ ํƒ€๊นƒ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋‹ค.

ํƒ€๊นƒ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ๋‹ค๋ฉด result๋ฅผ ๊ฐ€์ง€๊ณ  ์—ฌ๋Ÿฌ๊ฐ€์ง€๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๊ฒฐ๊ณผ๋ฅผ ๋กœ๊น…ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ๊ฒฐ๊ณผ๋กœ ๋ฐ›์€ ๊ฐ’์„ ๋ณ€ํ™˜ํ•ด์„œ ๋‹ค์‹œ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•ด์ค„ ์ˆ˜๋„ ์žˆ๋‹ค.

๋˜ํ•œ ๊ฒฐ๊ณผ๋กœ ๋ฐ›์€ ๊ฐ’์— ๋Œ€ํ•ด ๊ฒ€์ฆ์„ ํ•ด์ค„ ์ˆ˜๋„ ์žˆ๋‹ค.

return ํ•˜๋Š”๊ณณ

return ํ•˜๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ํƒ€๊นƒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ํƒ€๊นƒ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’์ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ผ๋ฉด ์ด๋ฅผ ํ˜ธ์ถœํ•œ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ž๋™ ํ”„๋ก์‹œ ๋“ฑ๋ก ๊ณผ์ •

์Šคํ”„๋ง ๋ถ€ํŠธ ์ž๋™ ์„ค์ •์œผ๋กœ "AnnotationAwareAspectJAutoProxyCreator" ์ด๋ผ๋Š” ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ๊ฐ€ ๋นˆ ๋“ฑ๋ก๋˜์–ด ์žˆ๋Š”๋ฐ, ์ด ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ๊ฐ€ @Aspect๊ฐ€ ๋ถ™์€ ํด๋ž˜์Šค๋ฅผ ๋ณด๊ณ  Advisor(์–ด๋“œ๋ฐ”์ด์ €)๋กœ ๋ณ€ํ™˜ํ•ด ์ €์žฅํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด Advisor(์–ด๋“œ๋ฐ”์ด์ €)๋ฅผ ๋ณด๊ณ  ํฌ์ธํŠธ์ปท์˜ ๋Œ€์ƒ์ด ๋˜๋Š” ๊ฒƒ๋“ค์„ "ProxyFactory"์— ์ธ์ž๋กœ ๋„˜๊ฒจ ์ž๋™์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ ์šฉํ•ด์ค€๋‹ค.

 

ProceedingJoinPoint๋Š” ๋ฌด์Šจ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ์–ด์„œ ํƒ€๊ฒŸ์— ๋Œ€ํ•œ ์ •๋ณด๋“ค์„ ์•Œ๊ณ  ์žˆ๋Š”์ง€?

ProceedingJoinPoint

๊ฒฐ๊ตญ ํ”„๋ก์‹œ์ด๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ผ“์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ˜ธ์ถœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํƒ€์ผ“์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋‹ˆ ์ด ์ •๋ณด๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๊ฒŒ ProceedingJoinPoint ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง AOP๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์›๋ณธ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋™์•ˆ ์ถ”๊ฐ€์ ์ธ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ProceedingJoinPoint ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ advice์— ์ „๋‹ฌํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ProceedingJoinPoint๋Š” ํ”„๋ก์‹œ๊ฐ€ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‹œ์ ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

์‹คํ–‰ - success()

ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋กœ๊ทธ๊ฐ€ ์ž˜ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

[log] void hello.aop.order.OrderService.orderItem(String)
[orderService] ์‹คํ–‰
[log] String hello.aop.order.OrderRepository.save(String)
[orderRepository] ์‹คํ–‰

์‹คํ–‰ ํ๋ฆ„

AOP - ํฌ์ธํŠธ์ปท ๋ถ„๋ฆฌํ•˜๊ธฐ

@Around์— ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์ง์ ‘ ๋„ฃ์„ ์ˆ˜ ๋„ ์žˆ์ง€๋งŒ, @Pointcut ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋ณ„๋„๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ๋„ ์žˆ๋‹ค.

@Slf4j
@Aspect
public class AspectV2 {

    //hello.aop.order ํŒจํ‚ค์ง€์™€ ํ•˜์œ„ ํŒจํ‚ค์ง€
    @Pointcut("execution(* hello.aop.order..*(..))")
    private void allOrder(){} //pointcut signature

    @Around("allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); //join point ์‹œ๊ทธ๋‹ˆ์ฒ˜
        return joinPoint.proceed();
    }
}

@Pointcut

@Pointcut์— ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

๋ฉ”์„œ๋“œ ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•ฉ์ณ์„œ ํฌ์ธํŠธ์ปท ์‹œ๊ทธ๋‹ˆ์ฒ˜(signature)๋ผ ํ•œ๋‹ค.
๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ void์—ฌ์•ผ ํ•œ๋‹ค. ์ฝ”๋“œ ๋‚ด์šฉ์€ ๋น„์›Œ๋‘”๋‹ค.

ํฌ์ธํŠธ์ปท ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” allOrder()์ด๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์ฃผ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ํฌ์ธํŠธ์ปท์ด๋‹ค.

@Around ์–ด๋“œ๋ฐ”์ด์Šค์—์„œ๋Š” ํฌ์ธํŠธ์ปท์„ ์ง์ ‘ ์ง€์ •ํ•ด๋„ ๋˜์ง€๋งŒ, ํฌ์ธํŠธ์ปท ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” @Around("allOrder()")๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

 

private, public ๊ฐ™์€ ์ ‘๊ทผ ์ œ์–ด์ž๋Š” ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด private์„ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ๋‹ค๋ฅธ ์• ์ŠคํŒฉํŠธ์—์„œ ์ฐธ๊ณ ํ•˜๋ ค๋ฉด public์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ AspectV1๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋ถ„๋ฆฌํ•˜๋ฉด ํ•˜๋‚˜์˜ ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์„ ์—ฌ๋Ÿฌ ์–ด๋“œ๋ฐ”์ด์Šค์—์„œ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ํด๋ž˜์Šค์— ์žˆ๋Š” ์™ธ๋ถ€ ์–ด๋“œ๋ฐ”์ด์Šค์—์„œ๋„ ํฌ์ธํŠธ์ปท์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์‹คํ–‰ - success()

ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋กœ๊ทธ๊ฐ€ ์ž˜ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

[log] void hello.aop.order.OrderService.orderItem(String) 
[orderService] ์‹คํ–‰
[log] String hello.aop.order.OrderRepository.save(String) 
[orderRepository] ์‹คํ–‰

AOP - ์–ด๋“œ๋ฐ”์ด์Šค ์ถ”๊ฐ€ ๊ตฌํ˜„

์•ž์„œ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ์— ์ถ”๊ฐ€๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•˜๋Š” ์ฝ”๋“œ๋„ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

์—ฌ๊ธฐ์„œ๋Š” ์ง„์งœ ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๊ฒ ๋‹ค.

@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()); //join point ์‹œ๊ทธ๋‹ˆ์ฒ˜
        return joinPoint.proceed();
    }

    //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() ์–ด๋“œ๋ฐ”์ด์Šค ์ ์šฉ

์‹คํ–‰ ์ˆœ์„œ

์‹คํ–‰ - 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)

ํฌ์ธํŠธ์ปท ๊ณต์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

ํฌ์ธํŠธ์ปท์„ ๊ณต์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ์™ธ๋ถ€ ํด๋ž˜์Šค์— ๋ชจ์•„๋‘์–ด๋„ ๋œ๋‹ค.

์ฐธ๊ณ ๋กœ ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ํฌ์ธํŠธ์ปท์˜ ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ public์œผ๋กœ ์—ด์–ด๋‘์–ด์•ผ ํ•œ๋‹ค.

public class Pointcuts {

    //hello.aop.order ํŒจํ‚ค์ง€์™€ ํ•˜์œ„ ํŒจํ‚ค์ง€
    @Pointcut("execution(* hello.aop.order..*(..))")
    public void allOrder(){} //pointcut signature

    //ํด๋ž˜์Šค ์ด๋ฆ„ ํŒจํ„ด์ด *Service
    @Pointcut("execution(* *..*Service.*(..))")
    public void allService(){}

    //allOrder && allService
    @Pointcut("allOrder() && allService()")
    public void orderAndService() {}
}

orderAndService(): allOrder() ํฌ์ธํŠธ์ปท์™€ allService() ํฌ์ธํŠธ์ปท์„ ์กฐํ•ฉํ•ด์„œ ์ƒˆ๋กœ์šด ํฌ์ธํŠธ์ปท์„ ๋งŒ๋“ค์—ˆ๋‹ค.

@Slf4j
@Aspect
public class AspectV4Pointcut {

    @Around("hello.aop.order.aop.Pointcuts.allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); //join point ์‹œ๊ทธ๋‹ˆ์ฒ˜
        return joinPoint.proceed();
    }

    @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());
        }
    }
}

์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํŒจํ‚ค์ง€๋ช…์„ ํฌํ•จํ•œ ํด๋ž˜์Šค ์ด๋ฆ„๊ณผ ํฌ์ธํŠธ์ปท ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ชจ๋‘ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

  • hello.aop.order.aop.Pointcuts.allOrder()
  • ํŒจํ‚ค์ง€๋ช….ํด๋ž˜์Šค์ด๋ฆ„

ํฌ์ธํŠธ์ปท์„ ์—ฌ๋Ÿฌ ์–ด๋“œ๋ฐ”์ด์Šค์—์„œ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ํšจ๊ณผ์ ์ด๋‹ค.

AOP - ์–ด๋“œ๋ฐ”์ด์Šค ์ˆœ์„œ ์ง€์ •ํ•˜๊ธฐ

์–ด๋“œ๋ฐ”์ด์Šค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•˜๊ณ  ์‹ถ์œผ๋ฉด @Aspect ์ ์šฉ ๋‹จ์œ„๋กœ org.springframework.core.annotation.@Order ์• ๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋ฌธ์ œ๋Š” ์ด๊ฒƒ์„ ์–ด๋“œ๋ฐ”์ด์Šค ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ผ ํด๋ž˜์Šค ๋‹จ์œ„๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ์ง€๊ธˆ์ฒ˜๋Ÿผ ํ•˜๋‚˜์˜ ์• ์ŠคํŽ™ํŠธ์— ์—ฌ๋Ÿฌ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์žˆ์œผ๋ฉด ์ˆœ์„œ๋ฅผ ๋ณด์žฅ ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค.

๋”ฐ๋ผ์„œ ์• ์ŠคํŽ™ํŠธ๋ฅผ ๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

 

ํ˜„์žฌ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ์ˆœ์„œ๊ฐ€ ์•„๋งˆ๋„ [doLog() -> doTransaction()]์ธ๋ฐ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ์–ด์„œ [doTransaction() -> doLog()] ๋˜๋„๋ก ๋ณ€๊ฒฝํ•ด๋ณด์ž.

@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()); //join point ์‹œ๊ทธ๋‹ˆ์ฒ˜
            return joinPoint.proceed();
        }
    }

    @Aspect
    @Order(1)
    public static class TxAspect {
        @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 ์• ๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์ˆซ์ž๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋จผ์ € ์‹คํ–‰๋œ๋‹ค.

์‹คํ–‰ ๊ฒฐ๊ณผ

[ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘] 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)

 

์–ด๋“œ๋ฐ”์ด์Šค ์ข…๋ฅ˜

@Slf4j
@Aspect
public class AspectV6Advice {

    @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());
            throw e;
        } finally {
            //@After
            log.info("[๋ฆฌ์†Œ์Šค ๋ฆด๋ฆฌ์ฆˆ] {}", joinPoint.getSignature());
        }
    }

    @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 doThrowing(JoinPoint joinPoint, Exception ex) {
        log.info("[ex] {} message={}", ex);
    }

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

์–ด๋“œ๋ฐ”์ด์Šค ์ข…๋ฅ˜

@Before

  • ์กฐ์ธ ํฌ์ธํŠธ ์‹คํ–‰ ์ „

@AfterReturning

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋  ๋•Œ ์‹คํ–‰

@AfterThrowing

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ์„œ ์ข…๋ฃŒ๋  ๋•Œ ์‹คํ–‰

@After

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์ข…๋ฃŒ๋˜๋ฉด ์‹คํ–‰๋œ๋‹ค. (finally๋ฅผ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.)
  • ์ •์ƒ ๋ฐ ์˜ˆ์™ธ ๋ฐ˜ํ™˜ ์กฐ๊ฑด์„ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.

@Around

  • ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰์˜ ์ฃผ๋ณ€์—์„œ ์‹คํ–‰๋œ๋‹ค.
  • ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „ํ›„์— ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์–ด๋“œ๋ฐ”์ด์Šค
  • ์–ด๋“œ๋ฐ”์ด์Šค์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ProceedingJoinPoint๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • proceed()๋ฅผ ํ†ตํ•ด ๋Œ€์ƒ์„ ์‹คํ–‰ํ•œ๋‹ค.
  • proceed()๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Œ(์žฌ์‹œ๋„) 

์ฐธ๊ณ  ์ •๋ณด ํš๋“

๋ชจ๋“  ์–ด๋“œ๋ฐ”์ด์Šค๋Š” org.aspectj.lang.JoinPoint๋ฅผ ์ฒซ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (์ƒ๋žตํ•ด๋„ ๋œ๋‹ค.)

๋‹จ @Around๋Š” ProceedingJoinPoint์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ฐธ๊ณ ๋กœ ProceedingJoinPoint๋Š” org.aspectj.lang.JoinPoint์˜ ํ•˜์œ„ ํƒ€์ž…์ด๋‹ค.

JoinPoint ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

  • getArgs(): ๋ฉ”์„œ๋“œ ์ธ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค
  • getThis(): ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • getTarget(): ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • getSignature(): ์กฐ์–ธ๋˜๋Š” ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • toString(): ์กฐ์–ธ๋˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์œ ์šฉํ•œ ์„ค๋ช…์„ ์ธ์‡„ํ•ฉ๋‹ˆ๋‹ค.

ProceedingJoinPoint ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

  • proceed(): ๋‹ค์Œ ์–ด๋“œ๋ฐ”์ด์Šค๋‚˜ ํƒ€์ผ“์„ ํ˜ธ์ถœํ•œ๋‹ค.

์‹คํ–‰ํ๋ฆ„

์ˆœ์„œ

  • ์Šคํ”„๋ง์€ 5.2.7 ๋ฒ„์ „๋ถ€ํ„ฐ ๋™์ผํ•œ @Aspect์•ˆ์—์„œ ๋™์ผํ•œ ์กฐ์ธํฌ์ธํŠธ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •ํ–ˆ๋‹ค.
  • ์‹คํ–‰ ์ˆœ์„œ: @Around, @Before, @After, @AfterReturning, @AfterThrowing
  • ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋˜๋Š” ์ˆœ์„œ๋Š” ์ด๋ ‡๊ฒŒ ์ ์šฉ๋˜์ง€๋งŒ, ํ˜ธ์ถœ ์ˆœ์„œ์™€ ๋ฆฌํ„ด ์ˆœ์„œ๋Š” ๋ฐ˜๋Œ€๋ผ๋Š” ์ ์„ ์•Œ์•„๋‘์ž.
  • ๋ฌผ๋ก  @Aspect์•ˆ์— ๋™์ผํ•œ ์ข…๋ฅ˜์˜ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ 2๊ฐœ ์žˆ์œผ๋ฉด ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ด ๊ฒฝ์šฐ ์•ž์„œ ๋ฐฐ์šด ๊ฒƒ ์ฒ˜๋Ÿผ @Aspect๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  @Order๋ฅผ ์ ์šฉํ•˜์ž.