모르지 않다는 것은 아는것과 다르다.

Spring

Spring AOP 1편 (With Spring Transaction)

채마스 2023. 4. 20. 21:08

개요

  • 나는 Spring AOP를 사용해서 어려가지 기능을 구현했다.
  • 하지만, Spring AOP의 정확한 동작원리에 대해서 알지는 못했다.
  • 그래서 이번기회에 Spring AOP의 꽃이라고 불리는 Spring Transaction(@Transactional) 코드를 예시로 분석해보면서 Spring AOP 에 대해서 공부해 보려고 한다.

 

Spring AOP

  • Spring Transaction 코드를 보기전에 먼저, Spring AOP 에 대해서 개념을 정리해보자.

  • 위의 그림을 보면, 프록시가 여러개의 어드바이저를 가지고 있고, 어드바이저는 1개의 포인트컷과 하나의 어드바이스를 가지고 있다.
  • 또한, 프록시는 Target도 가지고 있으며, Target의 메소드 하나하나가 조인포인트가 될 수 있다.
  • 포인트컷은 여러개의 조인포인트들을 필터링해가며, 실제로 어드바이스가 적용될 구간을 찾는다.
  • 위의 내용을 이해하기 위해서 프록시(Proxy), 어드바이스(Advice), 포인트컷(Pointcut), 어드바이저(Advisor) 에 대해서 개념을 정리해 보자.

 

프록시(Proxy)

  • SampleService라는 클래스가 있다고 가정하자.
  • 또한, SampleService에는 아래와 같이 save() 메소드가 있고, 메소드 위에는 @Transactional이라는 애노테이션이 붙어있다.
@Slf4j
public class SampleService {

    @Transactional
    public void save() {
        log.info("sample service save!");
    }
}
  • 아래의 코드는 프록시 패턴을 사용해서 만들어진 프록시 객체이다.
  • @Transactional 에노테이션이 붙은 클래스는 아래와 같은 형태로 프록시 객체가 생성될 것이다. (사실은 더 추상화되어 복잡하다.)
public class TransactionProxy {

    private SampleService target;

    public void logic() {

        //트랜잭션 시작
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {

            //SampleService 의 로직 수행
            target.save(); 

            transactionManager.commit(status);

        } catch (Exception e) {
            transactionManager.rollback(status);

            throw new IllegalStateException(e);
        }
    }
}
  • 코드에서 보면 실제 로직 target.save() 가 실행 되기 전후, 트랜잭션 관련 로직이 수행된 것을 확인할 수 있다.
  • 이렇게 프록시 객체를 사용해서 실제 타겟 메소드가 호출되기 전후로 원하는 기능을 넣을 수 있다.
  • 뒤에서 자세히 설명하겠지만, 위의 코드는 예시일 뿐이고 실제로는 어드바이저만 만들어주면, 자동으로 위와같은 프록시객체를 만들어준다.

어드바이스(Advice)

  • Advice는 프록시에 적용하는 부가기능 로직이다.

  • 위와 같이 MethodInterceptor 는 interceptor를 상속하고 있고, interceptor는 Advice를 상속하고있다.
  • 따라서 Advice를 구현하려면, MethodInterceptor 를 구현하면 된다.
  • invoke(MethodInvacation invocation) 메소드의 파라미터인 MethodInvacation에는 다음과 같은 것들이 포함되어있다.
    • 다음 메소드를 호출하는 방법
    • 현재 프록시 객체 인스턴스, 인수(args), 메소드 정보 등등...
  • 뒤에서 자세히 설명하겠지만, @Transactional의 부가기능을 담당하는 Advice는 TransactionInterceptor이며, 이 클래스 또한 MethodInterceptor를 구현하고 있다.

  • Advice도 간단한 예시를 들어보자.
@Slf4j
public class SampleAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        // 타겟 메소드 실행 전 로직
        log.info(invocation.getMethod().getName() + " before!");

        // 실제 타겟 메소드 실행
        Object result = invocation.proceed();


        // 타겟 메소드 실행 후 로직
        log.info(invocation.getMethod().getName() + " after!");

        return result;
    }
}
  • 위와 같이 MethodInterceptor를 상속받아서 Advice를 구현할 수 있다.

 

포인트컷(Pointcut)

  • 부가기능을 적용할 수 있는 구간 -> 이것을 조인포인트라고 한다.

참고로, 스프링 AOP의 조인 포인트는 항상 메소드 실행 지점으로 제한된다. -> 프록시 기반이기 때문 -> 프록시는 메소드가 실행되어야 실제 타겟을 호출하기 때문이다.

  • 그리고 이런 조인포인트들 중에서 실제로 부가기능이 적용될 구간을 결정(필터링)하는 구분자가 바로 포인트컷이다.
    • 주로 클래스와 메소드 이름으로 필터링한다.
  • 정리해보면, 포인트컷이 조인 포인트 중에서 어드바이스가 적용될 위치를 선별한다고 생각하면 된다.
  • 스프링이 기본적으로 다양한 포인트컷을 이미 제공해 주고 있기 때문에, 포인트컷을 직접 구현할 일은 많이 없다.
  • 가장 많이 사용되는 포인트 컷인 AspectJExpressionPointcut에 대해서 알아보자.

  • 포인트컷은 크게 ClassFilter 와 MethodMatcher 둘로 이루어진다.
    • 따라서 AspectJExpressionPointcut도 ClassFilter, IntroductionAwareMethodMatcher를 상속받고 있는 것을 확인할 수 있다.
    • ClassFilter
      • 특정 클래스가 포인트컷 표현식에 의해 나타낸 관심 영역에 속하는지 여부를 결정하는 역할을한다.
      • matches(Class) 메서드를 정의하며, 이 메서드는 주어진 클래스가 포인트컷 조건에 맞으면 true를 반환하고, 그렇지 않으면 false를 반환한다.
    • IntroductionAwareMethodMatcher
      • isCandidateForIntroduction(Class) 메서드에서 인터페이스에 대한 메서드 호출을 처리할 수 있는지 여부를 결정한다.
  • AspectJExpressionPointcut 외에도 아래와 같은 포인트컷이 주로 사용된다.
    • NameMatchMethodPointcut : 메서드 이름을 기반으로 매칭한다. 내부에서는 PatternMatchUtils를 사용한다.
    • JdkRegexpMethodPointcut : JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.
    • TruePointcut : 항상 참을 반환한다.
    • AnnotationMatchingPointcut : 애노테이션으로 매칭한다.
  • 포인트 컷은 크게 2가지로 사용된다.
    1. 애플리케이션 구동단계에서 프록시 적용여부를 판단하는데 사용된다.
      • 클래스, 메소드를 조건에 맞는지 체크한 다음 조건에 맞는다면, 프록시 객체를 생성한다. -> AOP를 적용하려면 프록시를 빈으로 등록해야되기 때문이다.
      • 뒤에서 언급하겠지만, AnnotationAwareAspectJAutoProxyCreator(빈 후처리기)가 포인트컷을 보고 프록시 객체를 생성할지말지 판단한다.
    2. 런타임에 어드바이스 적용여부를 판단하는데 사용된다.
      • 프록시가 호출 되었을 때 어드바이스를 적용할지 말지 포인트컷을 보고 판단한다.

 

어드바이저(Advisor)

  • 하나의 포인트컷 + 하나의 어드바이스 = 어드바이저
  • 따라서 어드바이저를 안다는 것은 부가기능(어드바이스)을 어디(포인트컷)에 적용할지 안다는 것 과 같다.

 

Spring AOP의 프록시, 어드바이스, 포인트컷, 어드바이저에 대해서 알아보았으니, Spring AOP를 구현하는 방법에 대해서 알아보자.