Spring AOP 의 원리

유알·2024년 8월 28일

기술 면접에서 잘 답변하지 못한 깊은 부분을 공부하며 정리한다.

Spring AOP 의 원리와 개념들을 공부한다.

기본 개념

// Aspect: 흩어진 관심사를 모듈화한 것 (여기서는 로그 기록 기능)
@Aspect
public class LoggingAspect {

    // PointCut: 특정 JointPoint를 선정하는 기준 (여기서는 "placeOrder()" 메서드 실행 전)
    @Pointcut("execution(* com.example.service.OrderService.placeOrder(..))")
    private void placeOrderMethod() {}

    // Advice: PointCut에 지정된 JointPoint에서 실행될 실제 부가기능 (여기서는 로그 출력)
    @Before("placeOrderMethod()")
    public void logBefore(JoinPoint joinPoint) {
        // JointPoint: Advice가 적용되는 지점 (여기서는 "placeOrder()" 메서드 실행 전)
        System.out.println("Executing method: " + joinPoint.getSignature().getName());
    }
}

// Target: Aspect를 적용하는 곳 (여기서는 OrderService 클래스)
public class OrderService {

    // 이 메서드가 Target이며, PointCut에 의해 선택된 JointPoint에서 Aspect가 적용됩니다.
    public void placeOrder() {
        System.out.println("Order placed successfully!");
    }
}
  • Aspect : 관심사를 모듈화 한것
  • Target : Aspect 가 영향을 미치는 곳(Class나 method 등)
  • Advice : 어떤 일을 해야할지
  • JointPoint : Advice가 실행될 수 있는 구체적 시점
  • PointCut : JointPoint을 정의한 것

AOP 작동 원리

기본적인 원리는 Proxy를 활용해서 동작한다. 다만 이 Proxy를 등록시키는 방법이 두가지가 있다.

JDK Dynamic Proxy

  • Dynamic Proxy 기술은 JDK 1.3 버전 부터 들어간 기술이며, java reflection으로 동작한다.
  • 단점으로는 Java Reflection의 공통적인 단점인 컴파일 시점 최적화를 거치지 않기 때문에 성능상 느리다.

Dynamic Proxy의 경우, 클래스에는 적용할 수 없고, 인터페이스를 대상으로만 사용할 수 있습니다.

CG Lib

  • CG Lib의 경우, Java의 바이트 코드를 생성하는 유명한 라이브러리 입니다.
  • 이 기술이 사용되게 된 이유를 알아야 하는데, 인터페이스가 아닌 클래스에 프록시를 적용시킬 필요가 있기 때문입니다.
  • 이는 레거시 코드나, 다른 라이브러리를 프록시로 감쌀때 자주 발생할 수 있습니다.
  • 방식은 아주 간단합니다. 해당 클래스를 상속하고 Override 하면 됩니다.

당연하게도, final 클래스나 final 메서드의 경우 상속 시 문제가 생기므로 이 방식을 적용할 수 없습니다.

나는 컴파일 시점에 적용된다고 착각하였으나, BeanPostProcessor에서 런타임에 바이트 코드를 생성하는 방식으로 작동한다.

적용 방식

AOP 프록시의 경우 빈 생명 주기 중, 콜백 호출 부분에 해당하는 BeanPostProcessor에서 생성되게 된다.

이 과정에서 프록시가 적용된다.

기본 프록시 전략

기본적으로는 어떤 프록시가 사용될까?

Spring 기본

  • Bean이 인터페이스를 구현하고 있으면, Dynamic Proxy 사용
  • 만약 인터페이스를 구현하고 있지 않으면, CG Lib 사용
  • 자동 프록시 생성기의 기본 옵션은 빈이 구현한 모든 인터페이스를 프록시 빈도 구현하게 한다.

Spring Boot

Spring Boot 의 경우 기본 옵션으로 테스트 해보면, 전부 CG Lib 프록시가 생성되는 것을 볼 수 있는데 그 이유는 Spring Boot 의 자동설정 때문이다.

TransactionAutoConfiguration 를 보면 CG Lib을 기본적으로 적용하도록 설정이 되어 있다.

주의점, 장단점 비교

Dynamic Proxy

구현체 참조 불가

Dynamic Proxy 를 통해 프록시를 생성하면, 세부 구현체가 감춰진다는 점을 주의해야 한다.
이 때문에 이 방식을 사용할 때는, DI 시 반드시 인터페이스를 통해 주입받도록 구현해야 한다. 그렇지 않다면, 기존에는 잘 작동하던 코드가 AOP 적용 후 주입이 불가능하다며 에러를 낼 것이다.

속도

리플렉션 특성상 느리다.

CG Lib

외부 의존성

이 방식을 사용할 때는, CG Lib에 대한 의존성을 추가해야한다. 그리고 내가 확인하기로는 Java 17+ 에 대한 지원을 종료하였다.

https://github.com/spring-projects/spring-framework/issues/12840

이 이슈를 보면, spring 5.0부터 cglib의 대안인 byte buddy 프록시가 추가되었다.

profile
더 좋은 구조를 고민하는 개발자 입니다

0개의 댓글