Spring AOP(old) Pointcut Implementation

이전에 살펴 봤듯이 포인트컷에는 두 가지 종류가 있습니다.

정적 포인트컷
– 직접 만들고 싶다면 StaticMethodMatcherPointcut 클래스를 상속하여 isMatch 메소드를 구현.
NameMatchMethodPointcut :: NameMatchMethodPointcutAdvisor가 이에 상응하는 Advisor입니다. 클래스 이름에서 알 수 있듯이 메소드 이름으로 해당 포인트컷을 지정합니다.

사용자 삽입 이미지
위 두개의 setter를 사용하여 값을 configuration metadata 상에서 injection하면 됩니다. 위에껀 매칭되는 메소드 이름 설정이 하나일때 사용하고 아래는 여러개 일때 사용합니다.

매칭되는 메소드 이름 설정은 메소드 이름 앞이나 뒤에 *을 사용할 수 있습니다. 예) order* 은 oder로 시작하는 모든 메소드.

Sta*Meth* 이런식으로 *을 메소드 이름 중간에 넣으면 못 찾나? – 이건 나중에 코딩해보면서 확인해봐야겠습니다.

Perl5RegexpMethodPointcut :: RegexpMethodPointcutAdvisor가 정규식을 사용한 포인트 컷을 받아 줄 수 있으며 JDK1.4 이상 부터는 기본으로 JdkRegexpMethodPointcut을 사용하는데 명시적으로 Perl5RegexpMethodPointcut을 사용할 수 도 있습니다.

NameMatchMethodPointcut과 다른 점은 무엇인가? – 정규 표현식을 사용한다는 것이고 메소드 이름으로만 포인트컷을 나타내는 것이 아니라 전체 패키지 경로와 클래스 이름까지 포함하여 나타내야 합니다.

Perl5RegexpMethodPointcut 과 JdkRegexpMethodPointcut 의 차이는 뭔가? – 둘 다 AbstractRegexpMethodPointcut 클래스를 상속한 자식 클래스들이며 정규 표현식으로 매칭되는 포인트컷을 정의할 때 사용합니다. Perl5RegexpMethodPointcut는 Perl5의 정규 표현식을 사용하며 Jakarta ORO
regular expression library가 필요합니다. 측 추가적인 jar파일이 필요한거죠. 하지만 JdkRegexpMethodPointcut 는 JDK 1.4 정규표현식을 사용하며 추가적인 jar파일은 필요 없고 jdk버전이 1.4이상일 때 사용가능 합니다.

정규 표현식은 어떻게 쓰는거지? – SIA에 잘 나와있군요. 쩜(.), 별(*), 더하기(+), 역슬래쉬(\) 네가지를 사용합니다.
쩜(.)은 어떤 문자든 딱 한 글자를 나타냅니다.
별(*)은 * 전에 있는 문자가 0 ~ 여러개 있을 수 있슴을 나타냅니다.
더하기(+)는 + 전에 있는 문자가 1 ~ 여러개 있을 수 있슴을 나타냅니다.
역슬래쉬(\)는 \ 다음에 오는 문자가 있어야 한다는 것을 나타냅니다.
.*set.* :: set이라는 글자가 들어있는 모든 메소드
.*get.+By.+ :: get뭐시기By모시기 라는 형태의 글자가 들어있는 모든 메소드
거의 암호 수준이군요. 쩜별(.*) 은 쩜이 0~여러개 있을 수 있다는 거니까 그말은 다시 어떤 문자든지 여러개 있을 수 있다는 거니까 저렇게 되는거죠.

동적 포인트컷
– MethodMatcher를 구현하고 isRuntime() 메소드가 true를 리턴하게 합니다. 실행 중에 포인트컷에 해당하는 모든 메소드를 만날때 마다 matches(Method, Class, Object[]) 가 호출되기 때문에 상당한 성능 저하[footnote]API를 보니 “Note that evaluating such pointcuts is 10-15 times slower than evaluating
normal pointcuts, but they are useful in some cases.” 이렇게 나와있네요.[/footnote]가 발생합니다.

ControlFlowPointcut :: 이 포인트컷을 위한 Advisor가 따로 존재하진 않고 이전에 찾아냈던 DefaultPointcutAdivisor로 Pointcut 빈과 Advice 빈을 묶어줍니다. AspectJ의 cflow 포인트 컷에 해당하는 일을 합니다. 현재 동작중인 쓰레드 스택에서 특정한 클래스나 메소드가 찾아 졌을 경우에만 true를 리턴하도록 설정이 가능합니다.
사용자 삽입 이미지이 생성자들을 사용하여 constuctor injection으로 쓰레드의 스택에서 어떤 타입의 어떤 메소드 이름의 메소드가 호출될 지점을 포인트컷으로 지정할 수 있습니다.

Spring AOP(old) Advisor

앞에서 살펴본 Advice와 Pointcut을 묶어서 하나의 Advisor로 만들 수 있습니다. 그 Advisor 하나를 보면 어디(Pointcut)에 뭐가(Advice) 적용될지 한눈에 보이게 됩니다.

이렇게 만든 Advisor 빈을 프록시 객체를 만들기 위한 ProxyFactoryBean에서 interceptorNames속성에 적어 주고, 타켓 빈을 target 속성에 적어 주고, 타켓 빈이 구현한 인터페이스를 proxyInterfaces에 적어 주면 프록시 bean을 사용할 수 있게 됩니다.

인터페이스가 없을 땐 CGLib을 사용한다고 했는데 어떻게 쓰는거지? 어딘가 예제가 나오겠지? 일단 지금은 그냥 인터페이스를 사용해서 프록시 객체 만드는 방법만…

사용자 삽입 이미지Pointcut과 Advice를 설정할 수 있는 가장 일반적인 Advisor 클래스는 무엇인가? – 찾아봐야겠네요.

AbstractBeanFactoryPointcutAdvisor, AbstractGenericPointcutAdvisor, AbstractPointcutAdvisor, AspectJExpressionPointcutAdvisor, AspectJPointcutAdvisor, DefaultBeanFactoryPointcutAdvisor, DefaultPointcutAdvisor, DynamicMethodMatcherPointcutAdvisor, NameMatchMethodPointcutAdvisor, PersistenceExceptionTranslationAdvisor, ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor, RegexpMethodPointcutAdvisor, StaticMethodMatcherPointcutAdvisor, TransactionAttributeSourceAdvisor

PointcutAdvisor 인터페이스를 구현한 클래스들 입니다. 이중에서 DefaultPointcutAdvisor가 setAdvice()와 setPointcut()을 가지고 있으니까 이녀석을 사용해서 따로 존재하는 pointcut과 advice를 합치면 될 것 같습니다.

보통은 PointcutAdvisor의 이름들을 보시면 Pointcut 이름 마다 거기에 해당하는 Advisor가 존재하는 것 같습니다. 그 말은 포인트컷 따로 어드바이스 따로 만들어서 DefaultPointcutAdvisor로 합치지 말고 어드바이스는 따로 만들고 포인트 컷은 Advisor의 속성에 직접 주입하는 방식으로 만들때 사용하라는 것 같습니다. 포인트 컷을 재사용할 것이 아니라면 그게 편하겠네요. 아마도 어드바이스는 재사용하기가 유리하지만 포인트컷은 해당 어플리케이션에 종속되기 쉬우니까 재사용이 어렵기 때문이겠죠.

Spring AOP(old) Pointcut

참조 : Reference 7.2, Spring In Action

Concept 
advice가 어떤 타입이든 상관없이 같은 포인트컷에 여러 adivce를 적용할 수 있습니다.

public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}

핵심이 되는 포인트컷 인터페이스로 클래스와 메소드로 포인트컷을 판단하게 되기 때문에 저렇게 나뉘어져 있습니다. 클래스 필터와 메소드 필터의 재사용성을 고려한거라고 볼 수 있습니다.

public interface ClassFilter {
    boolean matches(Class clazz);
}

인자로 받은 클래스 타입에 따라 true, false를 리턴하면 되겠죠. 이 것 보다는 훨씬 복잡하고 중요한 것이 메소드 필터 입니다.

public interface MethodMatcher {
    boolean matches(Method m, Class targetClass);
    boolean isRuntime();
    boolean matches(Method m, Class targetClass, Object[] args);
}

왜 클래스 필터가 있는데 클래스를 인자로 받을까요? – 모르겠네..흠…

위에서 부터 차례대로 프록시 객체를 생성할 때 호출이 되며 먼저 matches(Method, Class)를 호출해서 ture면 isRuntime()이 호출 되는 때 이 때 정적 포인트컷이면 false를 리턴하고 동적 포인트컷이면 true를 리턴합니다. isRuntime이 false면 세번째 메소드는 호출하지 않고 true면 매번 advice가 적용될 때 마다 호출 하여 확인하게 됩니다.
정적 포인트컷 : matches(Method, Class)로 딱 한번 실행 됩니다.
동적 포인트컷 : matches(Method, Class, Object[])는 매번 adivce가 적용 될 때 마다 실행 되서 체크하게 됩니다.

동적 포인트컷을 확인할 때 인자를 받는 이유는? – 메소드 이름과 클래스 이름이나 타입만 가지고는 확인하는데 필요한 정보가 부족한가. 왜 그걸론 부족하지? – 메소드와 클래스 정보로 부족하다면 정적 포인트컷은 왜 인자를 받아 오지 않는거지..

포인트컷 연산
– union :: 합집합
– intersaction :: 교집합
Pointcuts 클래스에 있는 메소드를 사용합니다. 하지만 2.0에 추가된 AspectJ의 표현식을 사용하면 더 간단하죠. || 나 && 이런 걸로 해결이 가능하니까요.

Spring AOP(old) Advice

참조 : SIA 3장

Spring이 제공하는 Advice 종류와 특징
1. arond advice :: MethodInterceptor :: 대상 메소드에 대한 호출을 가로챔
2. before advice :: BeforeAdvice :: 대상 메소드가 실행되기 전에 호출됨
3. after advice :: AfterReturningAdvice :: 대상 메소드가 리턴한 후에 호출됨
4. throws advice :: ThrowsAdvice :: 대상 메소드가 예외를 던질 때 호출됨

2.0 부터는 after finally 였나.. 대상 메소드가 끝났거나 예외가 발생했거나 종료하면 무조건 실행하는 Advice가 추가 됐습니다.

1. Before Advice 만들기
MethodBeforeAdvice 인터페이스를 구현합니다. 구현해야 할 메소드는 한 개 입니다.

void before(Method method,
            Object[] args,
            Object target)
            throws Throwable

    Callback before a given method is invoked.

    Parameters:
        method – method being invoked
        args – arguments to the method
        target – target of the method invocation. May be null.
    Throws:
        Throwable – if this object wishes to abort the call. Any exception thrown will be returned to the caller if it’s allowed by the method signature. Otherwise the exception will be wrapped as a runtime exception.

첫번째 인자는 대상이 되는 메소드
두번째는 그 메소드에 전달하는 인자들
세번째는 대상 메소드를 가지고 있는 객체

2. After Advice 만들기
AfterReturningAdvice 인터페이스를 구현합니다.

void afterReturning(Object returnValue,
                    Method method,
                    Object[] args,
                    Object target)
                    throws Throwable

    Callback after a given method successfully returned.

    Parameters:
        returnValue – the value returned by the method, if any
        method – method being invoked
        args – arguments to the method
        target – target of the method invocation. May be null.
    Throws:
        Throwable – if this object wishes to abort the call. Any exception thrown will be returned to the caller if it’s allowed by the method signature. Otherwise the exception will be wrapped as a runtime exception.

첫번째 인자에 대상 메소드의 리턴값이 추가 됐고 나머진 한칸씩 밀렸습니다.
메소드가 끝난 뒤 실행하는 Advice니까 가용한 정보가 하나 늘었습니다.

3. Throws Advice 만들기
ThrowsAdvice 인터페이스를 구현합니다.

void afterThrowing( ThrowableSubclass)
void afterThrowing(Method , args, target, ThrowableSubclass)

위에 있는 두 가지 적어도 한 가지는 구현해야 합니다.
궁금한건… 이 인터페이스는 마커 인터페이스인데.. 어떻게 둘 중 하나만 구현하거나 둘 다 구현해도 되게 만든건지 신기합니다.

4. Around Advice 만들기
MethodInterceptor 인터페이스를 구현합니다.

가장 자주 사용될 것 같은 Advice로 Spring의 인터페이스가 아닌 AOP alliance의 인터페이스 입니다. 따라서 다른 AOP alliance 진영에 속한 AOP 구현체에서도 이 것을 구현한 Advice를 재사용할 수 있겠습니다.

public java.lang.Object invoke(MethodInvocation invocation)
                        throws java.lang.Throwable

    Implement this method to perform extra treatments before and after the invocation. Polite implementations would certainly like to invoke Joinpoint.proceed().

    Parameters:
        invocation – the method invocation joinpoint
    Returns:
        the result of the call to Joinpoint.proceed(), might be intercepted by the interceptor.
    Throws:
        java.lang.Throwable – if the interceptors or the target-object throws an exception.

유일한 인자인 MethodInvocation에는 getMethod()가 있어서 Method 타입의 객체를 넘겨 받을 수 있습니다.

리턴 타입이 있는 이유? 이 Advice는 대상 메소드의 리턴값을 바꿀 수 있습니다. 대상 메소드의 수행 여부 또한 결정할 수 있는 매우 강력한 기능을 가진 Advice입니다.

대상 메소드 실행 여부는 어떻게 결정하는가? invocation.proceed(); 를 넣어 주면 실행이 되고 해당 메소드가 값을 리턴 한다면 리턴 값을 받을 수도 있겠죠.

5. Introduction Adice

공부해야 할 것

Spring 2.0 AOP

참조 : 3월달 마소 AOP 특집 중 토비님의 Spring 2.0 AOP

AOP를 구현 하는 방법.
1. AOP 언어 자체를 확장하는 방법 – AspectJ – 전용 컴파일러 필요함.
2. 그냥 자바 클래스에 설정파일이나 어노테이션을 사용하는 방법
    2-1. 컴파일 된 클래스를 변환하는 방법
    2-2. 클래스 로딩 시 바이트 코드를 조작하는 방법
    2-3. 순수한 자바 언어와 API만을 사용하는 방법 – Spring AOP

Spring AOP는 프록시 기반의 AOP 구현 방법을 사용.
1. 인터페이스에 대한 프록시 만들 때 – JDK의 Dynamic Proxy사용
2. 클래스에 대한 프록시 만들 때 – CGLib 사용

기존 Spring AOP.
1. 포인트컷(Pointcut)
    조인 포인트-Spring AOP는 메소드 실행 시점만 지원-의 묶음
    Spring 1.X 버전에서는 스태틱 포인트컷과 다이내믹 포인트컷 방식이 있다.

2. 어드바이스(Advice)
    Interception arount 어드바이스 : 메소드 실행 전 후 모두, 대상이 되는 메소드의 실행 여부 결정 가능 (ex, MethodInterceptor)
    Before 어드바이스 : 메소드 실행 전에
    Throws 어드바이스 : 메소드에서 예외 발생 했을 때
    After Return 어드바이스 : 메소드 실행 후에
    Introduction 어드바이스 : 기본의 클래스에 동적으로 필드나 메소드 추가

3. 어드바이저(Advisor)
    Advice + Pointcut = Aspect = 어드바이져(ex. DefaultPointcutAdvisor)

4. 프록시
    ProxyFactoryBean : target, proxyIntergace, interceptorNames 속성을 주입하여 사용.
    AutoProxyCreator(ex. BeanNameAutoProxyCreator) : BeanPostProcessor를 사용하여 임의의 빈에 다이내믹하게 프록시 설정 부여함.

5. 한계와 단점
    5-1. Pointcut 인터페이스 구현해야함. 포인트컷 표현, 적용이 어렵다.
    5-2. XML 설정이 복잡해 진다.
    5-3. IoC 컨테이너에 빈으로 등록되지 않은 객체는 적용할 수 없다.
    5-4. 약간의 성능저하
    5-5. 타깃 객체와 프록시가 분리되어 있다,

Spring 2.0 AOP.
1. AspectJ의 애스펙트 선언과 포인트컷 언어 도입
    한계와 단점에서 첫 번째와 두 번째 문제 해결

2. AspectJ AOP 이용방법
    AspectJ를 사용하여 세번째 문제 해결 가능.(@Configurable 사용)
    2-1. Spring 설정과 무관하게 사용.
    2-2. 에스팩트 정의를 빈으로 등록.(factory-method=”aspectOf” 사용)