ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 포인트컷, 어드바이스, 어드바이저 (Pointcut, Advice, Advisor)
    Spring 2022. 6. 17. 19:40

    용어 정리

    1. 포인트 컷 - Pointcut : 대상 여부 확인 필터 기능

    어디에 부가기능을 적용할지, 어디에 부가기능을 적용하지 않을지 판단하는 필터링 로직

    클래스와 메서드 이름으로 필터링을 한다.

     

    2. 어드바이스 - Advice : 부가 기능 로직 담당

    프록시가 호출하는 부가 기능으로, 프록시 로직

     

    3. 어드바이저 - Advisor : 포인트컷 + 어드바이스

    하나의 포인트 컷하나의 어드바이저를 가지고 있는 것

     

     

    위와 같이 3가지로 구분한 이유는 역할과 책임을 명확하게 분리하는 것이다.

    DefaultPointcutAdvisor advisor 
    	= new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice());
    
    /*
    DefaultPointcutAdvisor : Advisor 인터페이스의 가장 일반적인 구현체
                             하나의 포인트컷과 하나의 어드바이스를 생성자에 넣어주면 됨
                             
    Pointcut.TRUE : 항상 true를 반환하는 포인트 컷 (=필터 역할 X)
    */
    
    // 어드바이저 추가, 포인트 컷으로 어디에 어드바이스로 무엇을 적용할지 알 수 있음
    프록시팩토리.addAdvisor(advisor);

     

    직접 포인트 컷을 만들 경우 다음 인터페이스들을 구현하게 된다.

    // 포인트컷 클래스가 구현하는 인터페이스 두가지 모두 true를 반환해야 어드바이스 적용 가능
    public interface Pointcut {
     //클래스가 맞는지 확인 
     ClassFilter getClassFilter();
     
     //메서드가 맞는지 확인
     MethodMatcher getMethodMatcher();
    }
    
    public interface ClassFilter {
     boolean matches(Class<?> clazz);
    }
    
    public interface MethodMatcher {
     //method 정보를 가지고 일치여부 등을 통해 어드바이스 실행여부를 boolean으로 반환
     boolean matches(Method method, Class<?> targetClass);
     
     //true 반환 시 아래 메서드가 false 반환시 위 메서드가 사용된다.
     boolean isRuntime();
     
     // 매개변수가 동적으로 변경되므로 캐싱 못함
     boolean matches(Method method, Class<?> targetClass, Object... args);
    }

     

     

    우리가 필요한 포인트 컷은 스프링이 이미 대부분 많이 제공한다.

    // 스프링이 제공하는 포인트컷 : NameMatchMethodPointcut
    // 내부에서 PatternMatchUtils를 사용하기 때문에 *를 허용한다.
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.setMappedNames("*메서드이름*");
    
    // 스프링이 제공하는 포인트컷 : JdkRegexpMethodPointcut
    // JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.
    
    // 스프링이 제공하는 포인트컷 : TruePointcut
    // 항상 참을 반환한다.
    
    // 스프링이 제공하는 포인트컷 : AnnotationMatchingPointcut
    // 어노테이션으로 매칭한다.
    
    // 스프링이 제공하는 포인트컷 : AspectJExpressionPointcut
    // aspectJ 표현식으로 매칭한다.

    이때 중요한 것은 aspectJ 표현식으로 실무에서 사용하기 편하고 기능이 가장 많다. (AspectJExpressionPointcut)

     

     

    여러 어드바이저를 한번에 적용할 때

    클라이언트 -> 프록시1(어드바이저1) -> 프록시2(어드바이저2) -> 실제객체

     

    다음과 같은 플로우를 이끌어내기 위해 체인프록시를 생성한다고 할 때

    어드바이저는 하나의 포인트컷, 하나의 어드바이스만 가질 수 있으므로

    프록시를(즉 프록시 팩토리를) 적용하려는 어드바이스 수만큼 생성해야한다고 생각할 수 있다.

    하지만 이는 이전 프록시를 적용하는 클래스 개수만큼 생성해야하는 문제처럼 힘들고

    프록시 팩토리를 만드는 중복코드들이 계속해서 늘어난다.

     

    따라서 프록시를 하나만 만든 이후(프록시팩토리로)

    해당 프록시에 어드바이저를 추가하는 addAdvisor() 메서드를 사용할 수 있다.

     

    클라이언트 -> 프록시 -> 어드바이저1 -> 어드바이저2 -> 실제객체

     

    즉 이를 통해 어드바이저 갯수만큼 프록시를 만들때와 호출 순서는 같지만

    성능 상 효율적인 방법으로 어드바이저를 추가할 수 있다.

     

    따라서 스프링은 AOP를 적용할 때 최적화를 진행하여 지금처럼 프록시는 하나만 만들고

    하나의 프록시에 필요한 만큼의 어드바이저를 적용한다.

     

     

    프록시 팩토리를 활용한다면 프록시를 간편하게 생성 가능하고

    어드바이저, 어드바이스, 포인트컷을 통해 어떤 기능을 어디에 적용할지 명확하게 표현 가능하다.

     

    하지만 @Configuration을 통해 스프링 빈을 등록할 때 아직도 프록시를 적용하는 클래스의 수만큼

    스프링 빈을 생성해야한다.

     

    또한 @Component (@Controller, @Service, @Repository, @Configuration 등)

    애너테이션의 경우 컴포넌트 스캔을 통해 스프링 빈을 스프링 컨테이너에 등록하기 때문에

    지금까지 적용하는 방식으로 프록시를 대신 호출되도록 할 수 없다.

     

     

    이러한 문제를 해결하기 위한 방법이 빈 후처리기이다.

     

     

     

    [참고 : 인프런 - 스프링 핵심 원리 - 고급편]

Designed by Tistory.