Chapter 7. Spring AOP APIs

7.1. 소개

이전 장에서 @AspectJ를 사용하는 AOP에 관한 Spring 2.0 지원과 스키마 기반 aspect 정의를 설명하였다. 이번 장에서 우리는 저수준 Spring AOP APIs와 Spring 1.2 애플리케이션에서 사용된 AOP 지원을 논의 할 것이다. 새로운 애플리케이션에 대하여, 우리는 이전 장에서 설명한 Spring 2.0 AOP 지원의 사용을 권장한다. 그러나 기존 애플리케이션을 이용하여 작업을 하거나 도서와 기사를 읽을 때 여러분은 Spring 1.2 스타일 예제를 교차하여 참고해야 할 지도 모른다. Spring 2.0은 Spring 1.2 이전 버전 호환을 완벽히 하고 있다. 이전 장에서 설명한 모든것은 Spring 2.0에서 전부 지원한다.

7.2. Spring에서의 Pointcut API

어떻게 Spring이 결정적인 pointcut(교차점)개념을 처리하는지 보도록 하자.

7.2.1. 개념

Spring의 pointcut(교차점) 모델은 advice(충고) 유형의 pointcut 재사용 독립을 가능하게 한다. 동일한 pointcut(교차점)을 사용하여 다른 advice(충고)를 설정할 수 있다.

org.springframework.aop.Pointcut interface는 중심이 되는 인터페이스 이며, 특정한 클래스와 메서드에 advice들을 설정 하고는 했다. 아래에서 완성된 인터페이스를 보자:

public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

}

두개 부분으로 나눠진 Pointcut 인터페이는 클래스와 매치되는 부분을 갖는 메서드의 재사용과 매끄럽게 구성된 기능들(예를 들어 다른 메서드 matcher를 이용하여 "union(병합)" 수행을 허용한다.

주어진 타켓 클래스들의 세트에 pointcut(교차점)을 제한하기 위해 ClassFilter 인터페이스를 사용했다. matches() 메소드가 항상 true를 반환 한다면, 모든 타켓 클래스들을 매치 시킬 것이다:

public interface ClassFilter {

    boolean matches(Class clazz);
}

일반 적으로 MethodMatcher 인터페이스는 좀 더 중요한다. 아래에서 완성된 인터페이스를 보자:

public interface MethodMatcher {

    boolean matches(Method m, Class targetClass);

    boolean isRuntime();

    boolean matches(Method m, Class targetClass, Object[] args);
}

이 pointcut(교차점)이 타켓 클래스상에서 주어진 메서드를 매치하여 포함할 것인지 검사하기 위해 matches(Method, Class)을 사용한다. 모든 메서드 호출에 대하여 검사의 요구를 피하기 위해서 AOP 프록시를 생성할때 이 평가를 수행한다. 만일 인자 2개를 가진 matches 메서드가 주어진 메서드에 대해 true를 반환 한다면, 그리고 MethodMatcher에 대하여 isRuntime()메서드가 true를 반환하게 된다면, 모든 메서드 호출에 대하여 인자 3개를 가진 matches 메서드을 호출 할 것이다. 이것으로 pointcut(교차점)은 타켓 advice(충고)가 수행하기 위한 시점 바로 전에 메소드 호출에 전달된 인자들을 조사하는게 가능하게 한다.

대부분 MethodMatcher들은 정적이며, 그것들의 isRuntime() 메소드는 fasle를 반환함을 의미한다. 이 경우에, 인자 3개를 가진 matches 메서드를 결코 호출하지 않을 것이다.

[Tip]Tip

가능하다면, AOP프록시가 생성되었을때 AOP프레임워크가 pointcut평가의 결과를 캐시하는 것을 허용하도록 pointcut를 정적으로 만들어라.

7.2.2. pointcut에서의 기능

Spring은 pointcut에서의 기능을 지원한다. 특히 조합(union)교차(intersection).

  • 조합(Union)은 pointcut이 일치하는 메소드를 의미한다.

  • 교차(Intersection)는 두개의 pointcut이 서로 일치하는 메소드를 의미한다.

  • 조합(Union)은 대개 좀더 유용하다.

  • pointcut은 org.springframework.aop.support.Pointcuts 클래스내 정적 메소드나 같은 패키지내 ComposablePointcut 클래스를 사용하여 이루어질수 있다. 어쨌든, AspectJ pointcut표현을 사용하는 것이 대개 좀더 단순한 접근법이다.

7.2.3. AspectJ 표현 pointcuts

2.0 이후, Spring에 의해 사용되는 pointcut의 가장 중요한 타입은 org.springframework.aop.aspectj.AspectJExpressionPointcut이다. 이것은 AspectJ pointcut 표현 문자열을 파싱하기 위한 AspectJ 제공 라이브러리를 사용하는 pointcut이다.

제공되는 AspectJ pointcut에 대한 논의를 위해 앞장을 보라.

7.2.4. 편리한 pointcut 구현물

Spring은 다양하고 편리한 pointcut 구현물을 제공한다. 몇가지는 특별히 사용될수 있다. 다른 것들은 애플리케이션의 특성을 가지는 pointcut으로 하위클래스화되는 경향이 있다.

7.2.4.1. 정적인 pointcuts

정적인 pointcut은 메소드와 대상 클래스에 기초하고 메소드의 인자를 고려할수 없다. 정적인 pointcut은 충분하다. 그리고 대부분의 사용을 위해 제일좋다. 메소드가 처음으로 호출될때 Spring이 정적인 pointcut을 오직 한번 평가하는 것이 가능하다. 그리고 나서, 각각의 메소드 호출로 pointcut을 다시 평가할 필요는 없다.

Spring에 포함된 몇가지 정적인 pointcut구현물을 고려하자.

7.2.4.1.1. 정규 표현식 pointcut

정적인 pointcut을 명시하는 알기쉬운 방법은 정규표현식이다. Spring에 더하여 다양한 AOP프레임워크는 이 가능성을 만든다. org.springframework.aop.support.Perl5RegexpMethodPointcut은 Perl5 정규표현식 문법을 사용하는 일반적인 정규표현식 pointcut이다. Perl5RegexpMethodPointcut 클래스는 정규표현식을 일치시키기 위한 Jakarta ORO에 의존한다. Spring또한 JDK 1.4이상에서 지원되는 정규 표현식을 사용하는 JdkRegexpMethodPointcut 클래스를 제공한다.

Perl5RegexpMethodPointcut 클래스를 사용하여, 당신은 패턴 문자열의 목록을 제공할수 있다. 이러한 것들이 일치한다면, pointcut이 true로 평가될것이다(그래서 결과는 이러한 pointcut의 효과적인 조합이다. )

아래에서 사용법을 보여준다.

<bean id="settersAndAbsquatulatePointcut" 
    class="org.springframework.aop.support.Perl5RegexpMethodPointcut">
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</bean>

Spring은 advice를 참조하는 것을 허용하는 RegexpMethodPointcutAdvisor이라는 편리한 클래스를 제공한다(advice는 인터셉터, before advice, throws advice등이 될수 있다). Spring은 J2SE 1.4나 그 이상의 버전에서 JdkRegexpMethodPointcut를 사용할것이다. 그리고 이전 VM에서는 Perl5RegexpMethodPointcut를 사용할것이다. Perl5RegexpMethodPointcutperl5 프라퍼티를 true로 셋팅하여 강제로 사용할수 있다. 아래에서 보여주는 것과 같이 하나의 bean이 pointcut과 advice 모두를 캡슐화하는 것처럼 RegexpMethodPointcutAdvisor를 사용하는 것은 묶기(wiring)를 단순화한다.

<bean id="settersAndAbsquatulateAdvisor" 
    class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice">
        <ref local="beanNameOfAopAllianceInterceptor"/>
    </property>
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</bean>

RegexpMethodPointcutAdvisor는 어떤 advice타입과도 사용될수 있다.

7.2.4.1.2. 속성-지향 pointcuts

정적인 pointcut의 중요한 타입은 메타데이터-지향 pointcut이다. 이것은 대개 소스-레벨의 메타데이터인 메타데이터 속성의 값을 사용한다.

7.2.4.2. 동적 pointcuts

동적인 pointcut은 정적인 pointcut보다 평가하는데 좀더 가치가 있다. 그것들은 정적인 정보만큼 메소드 인자를 고려한다. 이것은 모든 메소드 호출시 평가되어야만 한다는 것을 의미한다. 결과는 인자가 다양한 만큼, 캐시될수 없다.

중요한 예제는 control flow pointcut이다.

7.2.4.2.1. 제어흐름(Control flow) pointcuts

Spring 제어흐름(control flow) pointcuts은 다소 덜 강력하지만 개념적으로 AspectJ cflow pointcuts과 유사하다(pointcut이 다른 pointcut에 의해 일치되는 join pointcut아래에서 수행되는 것을 명시하는 방법은 없다). 제어흐름(control flow) pointcut은 현재 호출 스택과 일치한다. 예를 들어, join point가 com.mycompany.web 패키지내 메소드나 SomeCaller 클래스에 의해 호출된다면 수행된다. 제어흐름(control flow) pointcut은 org.springframework.aop.support.ControlFlowPointcut 클래스를 사용하여 명시된다.

[Note]Note

제어흐름(control flow) pointcut은 다른 동적인 pointcut보다 런타임시 평가하는 것이 명백히 좀더 비싸다. Java 1.4에서, 비용은 대략 다른 동적인 pointcut의 다섯배이다. Java 1.3에서는 10배 이상이다.

7.2.5. Pointcut 슈퍼클래스

Spring은 자체적인 pointcut을 구현하는 것을 돕기 위한 유용한 pointcut 슈퍼클래스를 제공한다.

정적인 pointcut이 가장 유용하기 때문에, 당신은 아마도 아래에서 보여주는 것처럼 StaticMethodMatcherPointcut의 하위클래스를 만들것이다. 이것은 하나의 추상 메소드를 구현하는 것을 요구한다(비록 행위자를 사용자정의하기 위해 다른 메소드를 오버라이드하는 가능성이 있더라도).

class TestStaticPointcut extends StaticMethodMatcherPointcut {

    public boolean matches(Method m, Class targetClass) {
        // return true if custom criteria match
    }
}

여기에는 동적인 pointcut을 위한 슈퍼클래스가 있다.

당신은 Spring 1.0 RC2와 그 이상에서 advice타입을 가진 사용자정의 pointcut을 사용할수 있다.

7.2.6. 사용자정의 pointcuts

Spring AOP내 pointcut이 Java클래스이기 때문에, 정적이거나 동적이거나 사용자정의 pointcut을 선언하는 것이 가능하다. Spring내 사용자정의 pointcut은 임의로 복잡할수 있다. 어쨌든, AspectJ pointcut표현 언어를 사용하는 것은 가능하다면 추천된다.

[Note]Note

Spring의 차후 버전은 JAC에 의해 제공되는 것처럼 "의미론적인(semantic) pointcut"을 위한 지원을 제공한다. 예를 들어, "모든 메소드는 대상 객체내 인스턴스 변수를 변경한다."

7.3. Spring내 Advice API

Spring AOP가 advice를 다루는 방법을 보자.

7.3.1. Advice 생명주기

각각의 advice는 Spring bean이다. advice인스턴스는 모든 advice된 객체를 통해서나 각각의 advice된 객체에 유일하게 공유될수 있다. 이것은 클래스별 이나 인스턴스별 advice에 일치한다.

클래스별 advice는 가장 종종 사용된다. 이것은 트랜잭션 advisor와 같이 대개의 advice를 위해 적절하다. 이것은 프록시된 객체의 상태에 의존하지 않거나 새로운 상태를 추가한다. 그것들은 단순히 메소드와 인자에서 작동한다.

인스턴스별 advice는 믹스인(mixins)을 지원하기 위한 소개(introduction)를 위해 적절하다. 이 경우, advice는 프록시된 객체를 위한 상태를 추가한다.

같은 AOP프록시에서 공유되고 인스턴스별 advice를 혼합하여 사용하는 것은 가능하다.

7.3.2. Spring내 Advice 타입

Spring은 특별히 다양한 advice타입을 제공하고 임의의 advice타입을 지원하기 위해 확장가능하다. 기본적인 개념과 표준 advice타입을 보자.

7.3.2.1. Interception around advice

Spring내 가장 기본적인 advice타입은 interception around advice이다.

Spring은 메소드 인터셉션을 사용하여 around advice를 위한 AOP제휴 인터페이스와 호환된다. MethodInterceptors 구현 around advice는 다음의 인터페이스를 구현해야만 한다.

public interface MethodInterceptor extends Interceptor {
  
    Object invoke(MethodInvocation invocation) throws Throwable;
}

invoke() 메소드를 위한 MethodInvocation 인자는 호출되는 메소드, 대상 join point, AOP프록시 그리고 메소드를 위한 인자를 나타낸다. invoke() 메소드는 호출의 결과(join point의 반환값)를 반환해야만 한다.

간단한 MethodInterceptor 구현물은 다음처럼 보일것이다.

public class DebugInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]");
        Object rval = invocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

MethodInvocation의 proceed() 메소드를 호출하는 것을 노트하라. 이것은 join point를 향한 인터셉터 연쇄가 발생한다. 대부분의 인터셉터는 이 메소드를 호출하고 반환값을 반환할것이다. 어쨌든, around advice와 같은 MethodInterceptor는 proceed메소드를 호출하는 것보다 다른 값을 던지거나 예외를 던질수 있다. 어쨌든, 당신은 좋은 이유없이는 이것을 하길 원하지 않는다.

[Note]Note

MethodInterceptor는 다른 AOP 제휴-호환 AOP구현물과의 상호운용을 제공한다. 이 장의 나머지에서 언급되는 다른 advice타입은 Spring특성을 가지는 방법으로 공통적인 AOP개념을 구현한다. 대부분의 특정 advice타입을 사용하는 장점이 있는 반면에, 당신이 또다른 AOP프레임워크내 aspect를 수행하고자 한다면 MethodInterceptor around advice에 충실하라. pointcut은 프레임워크간의 상호작용하지 않고 AOP제휴은 pointcut인터페이스를 정의하지 않는다는 것을 노트하라.

7.3.2.2. Before advice

좀더 간단한 advice타입은 before advice이다. 이것은 메소드에 들어가기 전에만 호출되기 때문에 MethodInvocation객체가 필요하지 않다.

before advice의 중요한 장점은 proceed() 메소드를 호출할 필요가 없다는 것이다. 그러므로 인터셉터 연쇄를 처리하는 것을 실패하는 우연한 가능성이 없다.

아래에서 MethodBeforeAdvice 인터페이스를 보여준다. (비록 대개의 객체는 필드 인터셉션에 적용하고 Spring은 이것을 구현할것 같지 않더라도 Spring API 디자인은 필드 before advice를 허용할것이다.)

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;
}

반환타입이 void라는 것을 노트하라. before advice는 join point가 수행되기 전에 사용자정의 행위자를 추가할수 있다. 하지만 반환값을 변경할수는 없다. before advice가 예외를 던진다면, 이것은 인터셉터 연쇄의 수행을 취소할것이다. 예외는 인터셉터 연쇄를 위임할것이다. 체크되지 않았거나 호출된 메소드의 시그너처위라면, 클라이언트로 직접 전달될것이다. 그렇지 않다면, AOP프록시에 의해 체크되지 않은 예외로 포장될것이다.

모든 메소드 호출을 세는 Spring내 before advice의 예제이다.

public class CountingBeforeAdvice implements MethodBeforeAdvice {

    private int count;

    public void before(Method m, Object[] args, Object target) throws Throwable {
        ++count;
    }

    public int getCount() { 
        return count; 
    }
}
[Tip]Tip

Before advice는 어떤 pointcut과도 사용될수 있다.

7.3.2.3. Throws advice

Throws advice는 join point가 예외를 던질때, join point의 반환후에 호출된다. Spring은 타입화된 throws advice를 제공한다. 이것은 org.springframework.aop.ThrowsAdvice 인터페이스가 어떠한 메소드도 포함하지 않는다는 것을 의미한다. 이것은 주어진 객체가 하나 이상의 타입화된 throws advice메소드를 구현하는 태그 인터페이스 구분이다. 이것은 다음의 형태가 될것이다.

afterThrowing([Method], [args], [target], subclassOfThrowable) 

오직 마지막 인자만 필수이다. 메소드 시그너처는 advice메소드가 메소드나 인자에 관심을 가지는지에 대해 의존하는 하나의 인자에서 4개의 인자를 가지는 형태로 다양하다. 다음은 throws advice의 예제이다.

아래의 advice는 RemoteException(하위클래스를 포함해서)이 던져진다면 호출된다.

public class RemoteThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }
}

다음의 advice는 ServletException가 던져진다면 호출된다. 위 advice와는 달리, 이것은 4개의 인자를 선언한다. 그래서 호출된 메소드, 메소드 인자와 대상 객체에 접근한다.

public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something will all arguments
    }
}

마지막 예제는 이러한 두개의 메소드가 RemoteExceptionServletException를 다루는 한개의 클래스에서 사용되는 방법을 보여준다. 상당수의 throws advice메소드는 하나의 클래스에서 조합될수 있다.

public static class CombinedThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }
 
    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something will all arguments
    }
}
[Tip]Tip

Throws advice는 어떤 pointcut과도 사용될수 있다.

7.3.2.4. After Returning advice

Spring내 after returning advice는 아래에서 보여주는 것처럼 org.springframework.aop.AfterReturningAdvice 인터페이스를 구현해야만 한다.

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target) 
            throws Throwable;
}

after returning advice는 반환값(변경할수 없는), 호출된 메소드, 메소드 인자와 대상에 접근한다.

다음의 after returning advice는 예외를 던지지 않는 모든 성공적인 메소드 호출을 센다.

public class CountingAfterReturningAdvice implements AfterReturningAdvice {

    private int count;

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable {
        ++count;
    }

    public int getCount() {
        return count;
    }
}

이 advice는 수행경로를 변경하지 않는다. 예외를 던진다면, 이것은 반환값대신에 인터셉터 연쇄를 던질것이다.

[Tip]Tip

After returning advice는 어떠한 pointcut과도 사용될수 있다.

7.3.2.5. Introduction advice

Spring은 특별한 종류의 인터셉션 advice처럼 introduction advice를 처리한다.

Introduction은 다음의 인터페이스를 구현하는 IntroductionAdvisorIntroductionInterceptor를 요구한다.

public interface IntroductionInterceptor extends MethodInterceptor {

    boolean implementsInterface(Class intf);
}

AOP제휴 MethodInterceptor 인터페이스로부터 상속된 invoke() 메소드는 introduction을 구현해야만 한다. 소개된(introduced) 인터페이스에서 메소드가 호출된다면, 소개(introduction) 인터셉터가 메소드 호출을 다루는 책임을 진다. 이것은 proceed()를 호출할수 없다.

소개(Introduction) advice는 메소드 레벨보다는 클래스 레벨에서만 적용되는것처럼 어떠한 pointcut과도 사용될수 없다. 당신은 다음의 메소드를 가지는 IntroductionAdvisor를 가진 소개(introduction) advice만을 사용할수 있다.

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

	ClassFilter getClassFilter();

	void validateInterfaces() throws IllegalArgumentException;
}

public interface IntroductionInfo {

	Class[] getInterfaces();
}

여기에는 소개(introduction) advice와 관련된 MethodMatcher가 없다. 나아가 Pointcut도 없다. 오직 클래스 필터링이 논리적이다.

getInterfaces() 메소드는 이 advisor에 의해 소개된(introduced) 인터페이스를 반환한다.

validateInterfaces()메소드는 소개된(introduced) 인터페이스가 설정된 IntroductionInterceptor에 의해 구현될수 있는 있는지를 보기 위해 내부적으로 사용된다.

Spring 테스트 묶음으로부터 간단한 예제를 보자. 하나 이상의 객체를 위해 다음 인터페이스를 소개(introduce)하길 원한다고 가정해보자.

public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}

이것은 mixin을 보여준다. 우리는 타입이 무엇이든, lock과 unlock메소드를 호출하든 충고된(advised) 객체를 Lockable로 형변환할수 있도록 원한다. lock() 메소드를 호출한다면, LockedException를 던지기 위한 모든 setter메소드를 원한다. 게다가 우리는 객체를 불변으로 만들기 위한 능력을 제공하는 aspect를 추가할수 있다. 이것은 AOP의 좋은 예제이다.

첫째로, 우리는 IntroductionInterceptor가 필요할것이다. 이 경우, 편리한org.springframework.aop.support.DelegatingIntroductionInterceptor 클래스를 확장한다. 우리는 IntroductionInterceptor를 직접 구현할수 있다. 하지만 DelegatingIntroductionInterceptor를 사용하는 것이 대부분의 경우를 위해 가장 좋다.

DelegatingIntroductionInterceptor는 가로채기(interception)의 사용을 숨기고 소개된(introduced) 인터페이스의 실제 구현물을 위한 소개(introduction)를 위임하기 위해 디자인되었다. 위임은 생성자의 인자를 사용하여 어떠한 객체에도 셋팅될수 있다. 디폴트 위임(인자가 없는 생성자가 사용될때)은 이것이다. 게다가 아래의 예제에서, 위임은 DelegatingIntroductionInterceptorLockMixin 하위클래스이다. 위임이 주어졌을때, DelegatingIntroductionInterceptor 인스턴스는 위임에 의해 구현된 모든 인터페이스를 검색하고 인터페이스에 대한 소개(introduction)을 지원할것이다. LockMixin과 같은 하위클래스가 표시되지 않는 인터페이스를 억누르기 위한 suppressInterface(Class intf) 메소드를 호출하는 것이 가능하다. 어쨌든, IntroductionInterceptor가 지원하기 위해 준비되는 인터페이스가 얼마나 많은지는 문제가 되지 않는다. IntroductionAdvisor는 인터페이스가 실제로 나타나는 것을 제어할것이다. 소개된(introduced) 인터페이스는 대상에 의해 같은 인터페이스의 구현물을 숨길것이다.

게다가 LockMixin은 DelegatingIntroductionInterceptor의 하위클래스를 만들고 Lockable 자체를 구현한다. 슈퍼클래스는 Lockable이 소개(introduction)를 위해 제공될수 있는 것을 자동적으로 집어올린다. 그래서 우리는 명시할 필요가 없다. 우리는 이 방법으로 많은 인터페이스를 소개(introduce)할수 있다.

locked 인스턴스 변수의 사용을 노트하라. 이것은 대상 객체를 고정하기 위해 추가적인 상태를 효과적으로 추가한다.

public class LockMixin extends DelegatingIntroductionInterceptor 
    implements Lockable {

    private boolean locked;

    public void lock() {
        this.locked = true;
    }

    public void unlock() {
        this.locked = false;
    }

    public boolean locked() {
        return this.locked;
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
            throw new LockedException();
        return super.invoke(invocation);
    }

}

종종 이것은 invoke() 메소드를 오버라이드할 필요가 없다. DelegatingIntroductionInterceptor 구현물은 메소드가 소개(introduced)된다면 위임메소드를 호출하지만 그렇지 않다면 join point로 처리된다. 이것은 언제나 충분하다. 현재 상황에서, 우리는 체크를 추가할 필요가 있다. 잠김모드라면 호출될수 있는 setter메소드는 없다.

요구되는 소개(introduction) advisor는 단순하다. 해야할 필요가 있는 모든것은 구분되는 LockMixin 인스턴스를 가지고 소개된(introduced) 인터페이스(이 경우 Lockable)를 명시한다. 좀더 복잡한 예제는 소개(introduction) 인터셉터(프로토타입으로 정의될)에 대한 참조를 가진다. 이 경우, LockMixin에 관련된 설정은 없다. 그래서 우리는 new를 사용해서 이것을 간단히 생성한다.

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

    public LockMixinAdvisor() {
        super(new LockMixin(), Lockable.class);
    }
}

우리는 이 advisor를 매우 간단히 적용할수 있다. 이것은 설정을 요구하지 않는다(어쨌든 이것은 필요하다. IntroductionAdvisor없이 IntroductionInterceptor를 사용하는 것은 불가능하다). advisor는 상태유지인것처럼 인스턴스별이어야만 한다. 우리는 LockMixinAdvisor의 다른 인스턴스를 필요로 하고 나아가 각각의 advicsed객체를 위해 LockMixin을 필요로 한다. advisor는 advised된 객체의 상태의 일부로 이루어져있다.

우리는 다른 advisor처럼 Advised.addAdvisor() 메소드나 XML설정(추천방법이다)을 사용하여 이 advisor를 프로그램으로 적용할수 있다. 모든 프록시 생성의 선택은 소개(introduction)와 상태유지 믹스인(mixin)을 정확하게 다루는 "자동 프록시 생성자(auto proxy creator)"을 포함해서 아래에서 언급된다.

7.4. Spring내 Advisor API

Spring에서, Advisor는 pointcut표현에 관련된 하나의 advice객체를 포함하는 aspect이다.

소개(introduction)의 특별한 경우와 개별로, 어떤 advisor는 어떤 advice와도 사용될수 있다. org.springframework.aop.support.DefaultPointcutAdvisor는 가장 공통적으로 사용된 advisor클래스이다. 예를 들어, 이것은 MethodInterceptor, BeforeAdviceThrowsAdvice와 사용될수 있다.

이것은 Spring내 advisor와 advice타입을 같은 AOP프록시로 혼합하는 것이 가능하다. 예를 들어, 당신은 하나의 프록시 설정내 interception around advice, throws advice 와 before advice를 사용할수 있다. Spring은 필요한 생성 인터셉터 연쇄를 자동으로 생성할것이다.

7.5. AOP프록시를 생성하기 위한 ProxyFactoryBean사용하기

만약 비지니스 객체를 위해 Spring IoC컨테이너(ApplicationContext나 BeanFactory)를 사용한다면 그리고 사용해야만 한다면 당신은 Spring의 AOP FactoryBean중 하나를 사용하길 원할것이다. (factory빈은 다른 타입의 객체를 생성하는 것을 가능하게 하는 indirection의 레이어를 소개(introduce)한다는 것을 기억하라.)

[Note]Note

Spring 2.0 AOP 지원도 factory bean을 사용한다.

Spring내 AOP프록시를 생성하는 기본적인 방법은 org.springframework.aop.framework.ProxyFactoryBean을 사용하는 것이다. 이것은 적용할 pointcut와 advice의 완벽한 제어능력를 부여한다. 어쨌든 당신이 이러한 제어능력을 필요로 하지 않는다면 선호될수 있는 유사한 옵션들이 있다.

7.5.1. 기본

다른 Spring 구현물과 같은 ProxyFactoryBean은 indirection의 레벨을 소개한다. 만약 당신이 foo이름을 가진 ProxyFactoryBean를 정의한다면 foo를 참조하는 객체는 ProxyFactoryBean인스턴스 자신이 아니다. 하지만 객체는 getObject() 메소드의 ProxyFactoryBean's구현물에 의해 생성된다. 이 메소드는 대상 객체를 포장하는 AOP프록시를 생성할것이다.

AOP프록시를 생성하기 위해 ProxyFactoryBean나 다른 IoC형태로 감지되는 클래스를 사용하는 가장 중요한 이득중 하나는 이것이 IoC에 의해 advice와 pointcut가 관리될수 있다는 것이다. 이것은 다른 AOP프레임워크로는 달성하기 힘든 어떠한 접근법을 가능하게 하는 강력한 기능이다. 예를 들면 애플리케이션 객체(어떤 AOP프레임워크내 사용가능할수 있는 대상을 제외하고)를 참조할수 있는 advice는 의존성 삽입(Dependency Injection)의 의해 제공되는 모든 플러그인 가능한 능력으로 부터 이익을 취할수 있다.

7.5.2. JavaBean 프라퍼티

Spring에서 제공되는 대부분의 FactoryBean구현물처럼 ProxyFactoryBean은 자체가 자바빈이다. 프라퍼티는 다음을 위해 사용된다.

몇몇 핵심적인 프라퍼티는 모든 AOP프록시 factory를 위한 수퍼클래스인 org.springframework.aop.framework.ProxyConfig로부터 상속된다. 그것들은 다음을 포함한다.

  • proxyTargetClass: 만약 우리가 인터페이스보다는 대상 클래스를 프록시화 한다면 true이다. 만약 이 프라퍼티를 true로 셋팅한다면, CGLIB 프록시가 생성될것이다(Section 7.5.3, “JDK 와 CGLIB 기반의 프록시”를 보라)

  • optimize: 의욕적인 최적화가 CGLIB를 통해 생성된 프록시에 적용될지를 제어. 만약 당신이 관련 AOP프록시가 최적화를 다루는 방법을 이해하지 못한다면 이 셋팅을 사용하지 말라. 이것은 현재 CGLIB프록시를 위해서만 사용된다. 이것은 JDK동적 프록시에 대해 영향을 끼치지 않는다.(디폴트)

  • frozen: advice변경이 프록시 factory가 설정되었을때 허용되지 않을지에 대한 값. 디폴트는 false이다(이를테면, 프록시가 설정된후 프록시 설정에 대한 변경이 허용되지 않는다.).

  • exposeProxy: 현재 프록시가 대상에 의해 접근될수 있는 ThreadLocal으로 나타날수 있는지에 대한 값. (이것은 ThreadLocal을 위한 필요성 없이 MethodInvocation을 통해 사용가능하다.) 만약 대상이 프록시와 exposeProxy을 얻을 필요가 있다면 true이다. 대상은 AopContext.currentProxy()메소드를 사용할수 있다.

  • aopProxyFactory: 사용하기 위한 AopProxyFactory의 구현물. 동적 프록시, CGLIB또는 다른 프록시 전략을 사용할지에 대한 커스터마이징의 방법을 제공한다. 디폴트 구현물은 동적 프록시나 CGLIB를 선호한다. 이 프라퍼티를 사용할 필요는 없다. 이것은 Spring 1.1내 새로운 프록시 타입의 추가를 허용하는 경향이 있다.

ProxyFactory을 위해 명시하는 다른 프라퍼티는 다음을 포함한다.

  • proxyInterfaces: Spring인터페이스 이름의 배열. 만약 이것을 제공하지 않는다면 대상 클래스를 위한 CGLIB프록시는 사용될것이다(하지만 Section 7.5.3, “JDK 와 CGLIB 기반의 프록시”를 보라).

  • interceptorNames: Advisor의 문자열 배열, 적용하는 인터셉터나 다른 advice명. 정렬은 명백하다. 처음 들어온것을 처음 제공한다. 이것은 리스트내 첫번째 인터셉터가 처음으로 호출을 인터셉터할것이라는 것을 말한다.

    이름들은 조상 factory로 부터 bean 이름을 포함한 현재 factory내 bean 이름들이다. 당신은 advice의 싱글톤 셋팅을 무시하는 ProxyFactoryBean으로 결과를 내기 때문에 여기에 bean참조를 언급할수 없다.

    당신은 별표(*) 를 인터셉터 이름에 덧붙일수 있다. 이것은 적용될 별표(*) 앞에 부분으로 시작하는 이름으로 모든 advisor 빈즈의 애플리케이션으로 결과를 생성한다. 이 특징을 사용하는 예제는 Section 7.5.6, “'global' advisor 사용하기”에서 찾을수 있다.

  • 싱글톤 : factory가 하나의 객체를 반환하거나 하지 않거나 종종 getObject()메소드가 호출되는 것은 문제가 아니다. 다양한 FactoryBean구현물은 그러한 메소드를 제공한다. 디폴트 값은 true이다. 만약 당신이 상태유지(stateful) advice를 사용하길 원한다면 예를 들어 상태유지 mixin. false의 싱글톤 값을 가지고 프로토타입 advice를 사용하라.

7.5.3. JDK 와 CGLIB 기반의 프록시

이 부분은 ProxyFactoryBean이 특정 대상 객체를 위해 JDK 와 CGLIB 기반의 프록시중 하나를 생성하는 것을 선택하는 방법에 대해 명확한 문서를 제공한다.

[Note]Note

JDK와 CGLIB기반의 프록시를 생성하는 것과 관련하여 ProxyFactoryBean의 행위는 Spring의 1.2.x와 2.0사이에 변경되었다. ProxyFactoryBean는 현재 TransactionProxyFactoryBean 클래스의 어구처럼 자동-감지 인터페이스와 관련한 유사한 어구를 제시한다.

프록시가 되는 대상 객체의 클래스가 어떠한 인터페이스도 구현하지 않는다면, CGLIB기반의 프록시는 생성될것이다. JDK프록시는 인터페이스 기반이고 인터페이스가 없다는 것은 JDK프록시가 불가능하기 때문에 이것은 가장 쉬운 시나리오이다. 하나는 간단히 대상 bean으로 삽입되고 interceptorNames 프라퍼티를 통해 인터셉터의 목록을 명시한다. ProxyFactoryBeanproxyTargetClass 프라퍼티가 false로 셋팅된다면 CGLIB기반 프록시가 생성될것이다.(명백하게도 이것은 타당하지 않고 매우 장황하기 때문에 bean정의로부터 제거하는 것이 가장 좋다.)

대상 클래스가 하나(또는 그 이상)의 인터페이스를 구현한다면, 생성된 프록시의 타입은 ProxyFactoryBean의 설정에 의존한다.

ProxyFactoryBeanproxyTargetClass 프라퍼티가 true로 셋팅된다면, CGLIB기반의 프록시는 생성될것이다. 이것은 타당하고 최소한의 뜻밖의 상황이라는 개념을 유지한다. ProxyFactoryBeanproxyInterfaces 프라퍼티가 하나 이상의 전체 경로를 포함하는 인터페이스명으로 셋팅된다면, 사실 proxyTargetClass 프라퍼티는 true로 셋팅된다.

ProxyFactoryBeanproxyInterfaces 프라퍼티가 하나 이상의 전체 경로를 포함한 인터페이스명으로 셋팅된다면, JDK기반의 프록시가 생성될것이다. 생성된 프록시는 proxyInterfaces프라퍼티에 명시된 모든 인터페이스를 구현할것이다. 대상 클래스가 proxyInterfaces 프라퍼티에 명시된것보다 더 많은 인터페이스를 구현한다면, 이것은 잘되고 좋지만 이러한 추가적인 인터페이스는 반환된 프록시에 의해 구현되지 않을것이다.

ProxyFactoryBeanproxyInterfaces 프라퍼티가 셋팅되지 않지만 대상 클래스가 하나(또는 그 이상) 인터페이스를 구현한다면, ProxyFactoryBean은 대상 클래스가 적어도 하나의 인터페이스를 구현하고 JDK기반의 프록시가 생성된다는 사실을 자동-감지할것이다. 실제로 프록시가 될 인터페이스는 대상 클래스가 구현하는 모든 인터페이스가 될것이다. 사실, 이것은 대상 클래스가 proxyInterfaces 프라퍼티를 위해 구현하는 각각 그리고 모든 인터페이스의 목록을 제공한다. 어쨌든, 이것은 명백히 적은 작업을 수행하고 타이핑도 줄인다.

7.5.4. 프록시 인터페이스

액션내 ProxyFactoryBean의 간단한 예제를 보자. 이 예제는 다음을 포함한다.

  • 프록시 될 대상 bean. 이것은 밑의 예제내 "personTarget" bean 정의이다.

  • advisor와 advice를 제공하기 위해 사용되는 인터셉터

  • AOP프록시 bean정의는 적용할 advice에 따라 대상 객체(personTarget bean)와 프록시를 위한 인터페이스를 명시한다.

<bean id="personTarget" class="com.mycompany.PersonImpl">
    <property name="name"><value>Tony</value></property>
    <property name="age"><value>51</value></property>
</bean>

<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
    <property name="someProperty"><value>Custom string property value</value></property>
</bean>

<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>

<bean id="person" 
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces"><value>com.mycompany.Person</value></property>

    <property name="target"><ref local="personTarget"/></property>
    <property name="interceptorNames">
        <list>
            <value>myAdvisor</value>
            <value>debugInterceptor</value>
        </list>
    </property>
</bean>

interceptorNames 프라퍼티는 문자열 타입의 리스트(현재 factory내 인터셉터나 advisor의 bean이름들)를 가진다. advisor, 인터셉터, before, after return 그리고 throw advice객체는 사용될수 있다. advisor의 절렬은 중요하다.

[Note]Note

당신은 왜 리스트가 bean참조를 유지하지 않는지 이상할것이다. 이유는 만약 ProxyFactoryBean의 싱글톤 프라퍼티가 false로 셋팅된다면 이것은 비의존적인 프록시 인스턴스를 반환하는 것이 가능해야만 한다는 것이다. 만약 어느 advisor 자체가 프로토타입이라면 비의존적인 인스턴스는 반환될 필요가 있을것이다. 그래서 이것은 factory로 부터 프로토타입의 인스턴스를 얻는 것이 가능하게 되는 것이 필요하다. 참조를 유지하는 것은 중요하지 않다.

위의 "person" bean정의는 다음처럼 Person구현물을 대체하여 사용될수 있다.

Person person = (Person) factory.getBean("person");

같은 IoC컨텍스트내 다른 bean은 보통의 자바객체를 사용하는 것처럼 강력한 의존성을 표시할수 있다.

<bean id="personUser" class="com.mycompany.PersonUser">
  <property name="person"><ref local="person" /></property>
</bean>

이 예제내 PersonUser클래스는 Person타입의 프라퍼티를 드러낸다. 이것이 관여된만큼 AOP프록시는 "실제" person구현물을 대신해서 투명하게 사용될수 있다. 어쨌든 이 클래스는 동적 프록시 클래스가 될 것이다. 이것은 이것을 Advised인터페이스(밑에서 언급되는)로 형변환하는 것이 가능할 것이다.

다음처럼 익명의 내부 bean을 사용하는 대상과 프록시사이의 차이을 숨기는 것은 가능하다. 단지 ProxyFactoryBean정의만이 다르다. advice는 완성도를 위해서만 포함된다.

<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
  <property name="someProperty"><value>Custom string property value</value></property>
</bean>

<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>

<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces"><value>com.mycompany.Person</value></property>
  <!-- Use inner bean, not local reference to target -->
  <property name="target">
    <bean class="com.mycompany.PersonImpl">
      <property name="name"><value>Tony</value></property>
      <property name="age"><value>51</value></property>
    </bean>
  </property>
  <property name="interceptorNames">
    <list>
      <value>myAdvisor</value>
      <value>debugInterceptor</value>
    </list>
  </property>
</bean>

이것은 오직 Person타입의 객체만이 있다는 장점을 가진다. 우리가 애플리케이션 컨텍스트의 사용자가 un-advised객체에 대한 참조를 얻는것을 방지하길 원하거나 Spring IoC autowiring으로 어떤 모호함을 피할 필요가 있다면 유용하다. ProxyFactoryBean정의내 장점은 스스로 포함(self-contained)한다는 것이다. 어쨌든 factory로 부터 un-advised대상를 얻는것이 가능할때 실질적으로 장점이 될수 있다.

7.5.5. 프록시 클래스

만약 당신이 하나 이상의 인터페이스 보다 클래스를 프록시 할 필요가 있다면 무엇인가.?

위에 있는 우리의 예제를 생각해보라. Person인터페이스는 없다. 어떠한 비지니스 인터페이스도 구현하지 않는 Person을 호출하는 클래스를 advise할 필요가 있다. 이 경우 당신은 동적 프록시 보다 CGLIB프록시를 사용하기 위해 Spring을 설정할수 있다. 위 ProxyFactoryBean의 proxyTargetClass 프라퍼티를 간단하게 true로 셋팅하라. 클래스보다는 인터페이로 작업을 수행하는 것이 최상이지만 인터페이스를 구현하지 않는 클래스를 advise하는 기능은 기존 코드로 작업할때 유용할수 있다(일반적으로 Spring은 규범적이지 않다. 이것이 좋은 상황을 적용하는 것이 쉽다면 이것은 특정 접근법에 집중하는것을 피한다.)

만약 당신이 원한다면 당신이 인터페이스를 가지고 작업하는 경우조차도 CGLIB를 사용하는 것에 집중할수 있다.

CGLIB프록시는 수행시 대상 클래스의 하위 클래스를 생성함으로써 작동한다. Spring은 초기 대상에 메소드 호출을 위임하기 위한 생성된 하위클래스를 설정한다. 이 하위클래스는 advice내 짜여진 Decorator패턴을 구현한다.

CGLIB프록시는 사용자에게는 알기쉬워야한다. 어쨌든 여기에 생각해 볼 몇가지 사항들이 있다.

  • Final 메소드는 오버라이드가 될수 없는것처럼 advised 될수 없다.

  • 당신은 클래스패스내 CGLIB2 바이너리가 필요할것이다. 동적 프록시는 JDK와 함께 사용가능하다.

CGLIB프록시와 동적 프록시사이에는 작은 성능상의 차이가 있다. Spring 1.0에서 동적 프록시는 미세하게 좀더 빠르다. 어쨌든 이것은 차후에 변경될것이다. 성능은 이 경우에 명백하게 결정될수가 없다.

7.5.6. 'global' advisor 사용하기

인터셉터 이름에 별표(*) 를 덧붙임으로써 별표(*)앞의 부분과 대응되는 bean이름을 가지는 모든 advisor는 advisor연계에 추가될것이다. 이것은 당신이 표준적인 'global' advisor의 모음을 추가할 필요가 있다면 편리하다.

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="target" ref="service"/>
  <property name="interceptorNames">
    <list>
      <value>global*</value>
    </list>
  </property>
</bean>

<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>

7.6. 간결한 프록시 정의

특별히 트랜잭션 성질을 가지는 프록시를 정의할때 당신은 많은 유사한 프록시 정의로 끝낼지도 모른다. 내부 bean정의에 따라 부모및 자식 bean정의의 사용은 좀더 깔끔하고 좀더 갈결한 프록시 정의라는 결과를 만들수 있다.

첫번째 부모, 템플릿, bean 정의는 프록시를 위해 생성된다.

<bean id="txProxyTemplate" abstract="true"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="transactionManager" ref="transactionManager"/>
  <property name="transactionAttributes">
    <props>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>

이것은 자체적으로 인스턴스화가 결코 될수 없다. 그래서 실제로 완벽하지 않을지도 모른다. 생성될 필요가 있는 각각의 프록시는 내부 bean정의처럼 프록시의 대상을 포장하는 자식 bean정의이다. 대상은 자기 자신의 것이 결코 사용되지 않을것이다.

<bean id="myService" parent="txProxyTemplate">
  <property name="target">
    <bean class="org.springframework.samples.MyServiceImpl">
    </bean>
  </property>
</bean>

트랜잭션 위임(propagation) 셋팅과 같은 경우처럼 부모 템플릿으로부터 프라퍼티를 오버라이드 하는것은 물론 가능하다.

<bean id="mySpecialService" parent="txProxyTemplate">
  <property name="target">
    <bean class="org.springframework.samples.MySpecialServiceImpl">
    </bean>
  </property>
  <property name="transactionAttributes">
    <props>
      <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
      <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
      <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
      <prop key="store*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>

위 예제에서 우리는 부모 bean정의를 앞서 서술된것처럼 abstract 속성을 사용해서 abstract처럼 명시적으로 표시한다. 그래서 이것은 실질적으로 인스턴스화 된 적이 없을것이다. 애플리케이션 컨텍스트(간단한 bean factory는 아닌)는 디폴트로 모든 싱글톤이 미리 인스턴스화될것이다. 그러므로 이것은 당신이 템플릿처럼 사용할 경향이 있는 (부모) bean정의를 가지고 이 정의가 클래스를 명시한다면 당신은 abstract속성을 true로 셋팅해야만 하고 반면에 애플리케이션 컨텍스트는 실질적으로 이것을 먼저 인스턴스화할 것이다.

7.7. ProxyFactory로 프로그램으로 AOP프록시를 생성하기.

Spring을 사용해서 프로그램으로 AOP프록시를 생성하는 것은 쉽다. 이것은 당신에게 Spring IoC에서 의존성없이 Spring AOP를 사용하는것을 가능하게 한다.

다음의 리스트는 하나의 인터셉터와 하나의 advisor로 대상 객체를 위한 프록시의 생성을 보여준다. 대상 객체에 의해 구현된 인터페이스는 자동적으로 프록시화될것이다.

ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addInterceptor(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();

첫번째 단계는 org.springframework.aop.framework.ProxyFactory 타입의 객체를 생성하는 것이다. 당신은 위 예제처럼 대상 객체와 함께 이것을 생성할수 있거나 대안이 되는 생성자로 프록시될 인터페이스를 명시할수 있다.

당신은 인터셉터나 advisor을 추가할수 있고 ProxyFactory의 생명을 위해 그것들을 조작할수 있다. 만약 당신이 IntroductionInterceptionAroundAdvisor를 추가한다면 당신은 추가적인 인터페이스를 위한 프록시를 야기할수 있다.

또한 ProxyFactory에는 당신에게 before advice와 throw advice와 같은 다른 advice타입을 추가하도록 허용하는 편리한 메소드(AdvisedSupport로부터 상속된)가 있다. AdvisedSupport는 ProxyFactory와 ProxyFactoryBean모두의 수퍼클래스이다.

[Tip]Tip

IoC프레임워크를 사용해서 AOP프록시 생성을 통합하는 것은 대부분의 애플리케이션에서 최상의 선택이다. 우리는 당신이 일반적인 것처럼 AOP를 사용해서 자바코드로 부터 설정을 구체화하는것을 추천한다.

7.8. advised 객체 조작하기.

당신이 AOP프록시를 생성한다고 해도 당신은 org.springframework.aop.framework.Advised인터페이스를 사용해서 그것들을 조작할수 있다. 어떤 AOP프록시가 다른 어또한 인터페이스를 구현한다고 해도 이 인터페이스로 형변환될수 있다. 이 인터페이스는 다음의 메소드를 포함한다.

Advisor[] getAdvisors();

void addAdvice(Advice advice) throws AopConfigException;

void addAdvice(int pos, Advice advice) 
        throws AopConfigException;

void addAdvisor(Advisor advisor) throws AopConfigException;

void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

int indexOf(Advisor advisor);

boolean removeAdvisor(Advisor advisor) throws AopConfigException;

void removeAdvisor(int index) throws AopConfigException;

boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

boolean isFrozen();

getAdvisors() 메소드는 모든 advisor, 인터셉터 또는 factory에 추가될수 있는 다른 advice타입을 위해 advisor을 반환할것이다. 만약 당신이 advisor를 추가한다면 이 시점에 반환되는 advisor는 당신이 추가한 객체가 될것이다. 만약 당신이 인텃베터나 다른 advice타입을 추가한다면 Spring은 이것을 언제나 true를 반환하는 pointcut를 가지고 advisor로 포장할것이다. 게다가 당신이 MethodInterceptor을 추가한다면 이 시점을 위해 반환된 advisor는 당신의 MethodInterceptor과 모든 클래스와 메소드에 대응되는 pointcut을 반환하는 DefaultPointcutAdvisor가 될것이다.

addAdvisor()메소드는 어떤 advisor을 추가하기 위해 사용될수 있다. 언제나 pointcut와 advice를 유지하는 advisor는 어떤 advice나 pointcut(introduction은 아닌)와 사용될수 있는 일반적인 DefaultPointcutAdvisor이 될것이다.

디폴트에 의해 한번 프록시가 생성되었을때 advisor나 인터셉터를 추가하거나 제거하는것은 가능하다. 오직 제한은 factory로 부터 존재하는 프록시가 인터페이스 변경을 보여주지 않을것이라는 것처럼 introduction advisor을 추가하거나 제거하는것이 불가능하다.(당신은 이 문제를 피하기 위해 factory로 부터 새로운 프록시를 얻을수 있다.)

AOP프록시를 Advised 인터페이스로 형변환하고 이것의 advice를 시험하고 조작하는것의 간단한 예제이다.

Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");

// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(new DebugInterceptor());

// Add selective advice using a pointcut
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));

assertEquals("Added two advisors",
     oldAdvisorCount + 2, advised.getAdvisors().length);
[Note]Note

이것은 비록 정확한 사용법이 의심스럽지 않더라도 제품내 비지니스 객체의 advice를 변경하는 것이 현명한것인지(advisable) 아닌지 의심스러울 것이다. 어쨌든 이것은 개발에서 매우 유용할수 있다. 예를 들면, 테스트에서 내가 테스트하길 원하는 메소드 호출내에서 얻어지는 인터셉터나 다른 advice의 형태내 테스트코드를 추가하는것이 가능하게 되는것이 매우 유용하다는 것을 때때로 발견하곤 한다.(예를 들어, advice는 그 메소드(예를 들면, 롤백을 위해 트랜잭션을 표시하기 전에 데이터베이스가 정확하게 수정되었는지 체크하기 위해 SQL수행하는것) 생성된 트랜잭션 내부에서 얻어질수 있다.

당신이 프록시를 생성하는 방법에 대해 의존하라. 당신은 Advised isFrozen()메소드가 true를 반환하고 추가나 삭제를 통해 advice를 변경하는 어떤 시도가 AopConfigException을 결과로 보내는 경우에 frozen 플래그를 셋팅할수 있다. advised 객체의 상태를 고정하기 위한 능력은 몇몇 경우에 유용하다. 예를 들면 보안 인터셉터를 제거하는 호출 코드를 제한하기 위해. 이것은 아마도 수행 advice 변경이 요구되지 않는다면 공격적인 최적화를 허용하기 위해 Spring 1.1내에서 사용된다.

7.9. "autoproxy" 기능 사용하기

지금까지 우리는 ProxyFactoryBean이나 유사한 factory bean을 사용해서 AOP프록시의 명시적인 생성을 설명했다.

Spring은 또한 자동적으로 선택된 bean정의를 프록시할수 있는 "autoproxy" bean정의를 사용하는 것을 허용한다. 이것은 Spring에 컨테이너 로드처럼 어떤 bean정의의 변경을 가능하게 하는 "bean 후 처리자" 구조로 내장되었다.

이 모델에서 당신은 자동 프록시 내부구조를 설정하는 XML bean정의파일내 몇몇 특별한 bean정의를 셋업한다. 이것은 당신에게 자동프록시를 위해 적당한 대상을 선언하도록 허용한다. 당신은 ProxyFactoryBean을 사용할 필요가 없다.

이것을 하기 위한 두가지 방법이 있다.

  • 현재 컨텍스트내 bean을 명시하기 위해 참조하는 autoproxy 생성자 사용하기.

  • 개별적으로 검토되기 위한 가치가 있는 autoproxy 생성의 특별한 경우. autoproxy 생성은 소스레벨 메타데이타 속성에 의해 이루어진다.

7.9.1. autoproxy bean정의

org.springframework.aop.framework.autoproxy패키지는 다음의 표준적인 autoproxy 생성자를 제공한다.

7.9.1.1. BeanNameAutoProxyCreator

BeanNameAutoProxyCreator는 이름과 문자값또는 와일드카드가 들어맞는 bean을 위해 자동적으로 AOP프록시를 생성한다.

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames"><value>jdk*,onlyJdk</value></property>
  <property name="interceptorNames">
    <list>
      <value>myInterceptor</value>
    </list>
  </property>
</bean>

ProxyFactoryBean를 사용하는것처럼 프로토타입 advisor을 위해 정확한 행위를 허용하는 인터셉터의 리스트보다 interceptorNames 프라퍼티가 있다. 명명된 "인터셉터"는 advisor나 다른 advice타입이 될수 있다.

일반적으로 자동 프록시를 사용하는 것처럼 BeanNameAutoProxyCreator를 사용하는 중요한 점은 다중객체에 일관적으로 같은 설정을 적용하고 최소한의 설정을 가진다. 이것은 다중 객체에 선언적인 트랜잭션을 적용하기 위해 널리 알려진 선택이다.

위 예제에서 "jdkMyBean" 과 "onlyJdk"처럼 이름이 대응되는 bean정의는 대상 클래스를 가지는 명백한 옛 bean정의이다. AOP프록시는 BeanNameAutoProxyCreator에 의해 자동적으로 생성될것이다. 같은 advice는 모든 대응되는 bean에 적용될것이다. 만약 advisor가 사용된다면(위 예제안의 인터셉터보다) pointcut은 다른 bean에 다르게 적용될것이다.

7.9.1.2. DefaultAdvisorAutoProxyCreator

좀더 일반적이고 굉장히 강력한 자동 프록시 생성자는 DefaultAdvisorAutoProxyCreator이다. 이것은 autoproxy advisor의 bean정의내 특정 bean이름을 포함할 필요없이 현재 컨텍스트에 적절한 advisor를 자동적으로 적용할것이다. 이것은 일관적인 설정의 장점과 BeanNameAutoProxyCreator처럼 중복의 회피를 제공한다.

이 기법을 사용하는 것은 다음을 포함한다.

  • DefaultAdvisorAutoProxyCreator bean정의를 명시하기.

  • 같거나 관련된 컨텍스트내 많은 수의 advisor명시하기. 인터셉터나 다른 advice가 아닌 advisor이 되어야만 한다. 이것은 평가하기 위한, 후보 bean정의를 위해 각각의 advice의 적절함을 체크하기 위한 pointcut가 되어야만 하기 때문에 필요하다.

DefaultAdvisorAutoProxyCreator는 각각의 비지니스 객체(예제내에서 "businessObject1" 과 "businessObject2" 와 같은) 를 적용해야하는 advice가 무엇인지 보기 위해 각각의 advisor내 포함된 pointcut를 자동적으로 평가할것이다.

이것은 많은 수의 advisor가 각각의 비지니스 객체에 자동적으로 적용될수 있다. 만약 어떠한 advisor내 pointcut이 비지니스 객체내 어떠한 메소드와도 대응되지 않는다면 객체는 프록시화 되지 않을것이다. bean정의가 새로운 비지니스 객체를 위해 추가된다면 그것들은 필요할때 자동적으로 프록시화될것이다.

일반적인 자동프록시는 un-advised객체를 얻기 위한 호출자나 의존적인 것을 불가능하게 만드는 장점을 가진다. 이 ApplicationContext의 getBean("businessObject1")을 호출하는 것은 대상 비지니스 객체가 아닌 AOP프록시를 반환할것이다. ("내부 bean" 표현형식은 좀더 빨리 보여지고 또한 이 이득을 제공한다.)

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
  <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>

<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>

<bean id="businessObject1" class="com.mycompany.BusinessObject1">
  <!-- Properties omitted -->
</bean>

<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>

DefaultAdvisorAutoProxyCreator는 만약 당신이 많은 비지니스 객체에 일관적으로 같은 advice를 적용하길 원한다면 매우 유용하다. 내부구조 정의가 대체될때 당신은 특정 프록시 설정을 포함하는것 없이 새로운 비지니스 객체를 간단하게 추가할수 있다. 당신은 예를 들어 설정의 최소한의 변경으로 추적및 성능 모니터링 aspect처럼 추가적인 aspect를 매우 쉽게 감소시킬수있다.

DefaultAdvisorAutoProxyCreator는 필터링과 정렬을 위한 지원을 제공한다.(명명 규칙을 사용하는 것은 같은 factory내 AdvisorAutoProxyCreators를 오직 특정 advisor가 평가하고, 다중 사용을 허용하고, 다르게 설정된다.) advisor는 이것이 쟁점이라면 정확한 정렬을 보장하기 위한 org.springframework.core.Ordered 인터페이스를 구현할수 있다. 위 예제에서 사용된 TransactionAttributeSourceAdvisor는 설정가능한 정렬값을 가지지만 디폴트는 정렬되지 않는다.

7.9.1.3. AbstractAdvisorAutoProxyCreator

이것은 DefaultAdvisorAutoProxyCreator의 수퍼클래스이다. advisor정의가 프레임워크 DefaultAdvisorAutoProxyCreator의 행위를 위해 부족한 사용자정의을 제공하는 가능성이 희박한 경우에 당신은 이 클래스를 하위클래스화하여 당신 자신의 autoproxy생성자를 생성할수 있다.

7.9.2. 메터데이타-지향 자동 프록시 사용하기.

autoproxy의 특별히 중요한 타입은 메타데이타에 의해 다루어진다. 이것은 .NET ServicedComponents에 유사한 프로그래밍 모델을 생산한다. EJB처럼 XML배치 서술자를 사용하는 대신에 트랜잭션 관리와 다른 기업용 서비스를 위한 설정은 소스레벨 속성내 유지된다.

이 경우 당신은 메터데이타 속성을 이해하는 advisor와의 조합으로 DefaultAdvisorAutoProxyCreator을 사용한다. 이 메터데이타는 autoproxy생성 클래스 자체보다는 후보 advisor의 pointcut부분내 유지됨을 명시한다.

이것은 DefaultAdvisorAutoProxyCreator의 특별한 경우이다. 하지만 그것 자신의 보상을 할만하다. (메터데이타-인식 코드는 AOP프레임워크 자체가 아닌 advisor내 포함된 pointcut내에 있다.)

JPetStore샘플 애플리케이션 의 /attributes디렉토리는 속성-지향 자동프록시의 사용을 보여준다. 이 경우 TransactionProxyFactoryBean를 사용할 필요는 없다. 메터데이타-인식 pointcut의 사용이기 때문에 간단하게 비지니스 객체의 트랜잭션적인 속성을 정의하는 것은 춘분하다. bean정의는 /WEB-INF/declarativeServices.xml내 다음의 코드를 포함한다. 이것은 일반적이고 JPetStore밖에서 사용될수 있다.

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
  <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>

<bean id="transactionInterceptor"
    class="org.springframework.transaction.interceptor.TransactionInterceptor">
  <property name="transactionManager" ref="transactionManager"/>
  <property name="transactionAttributeSource">
    <bean class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource">
      <property name="attributes" ref="attributes"/>
    </bean>
  </property>
</bean>

<bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/>

DefaultAdvisorAutoProxyCreator bean정의(이름은 명백하지 않다. 나아가 이것은 생략될수 있다.)는 현재 애플리케이션 컨텍스트내 모든 적절한 pointcut을 가져올것이다. 이 경우 "autoProxyCreator"라고 불리는 DefaultAdvisorAutoProxyCreator bean정의는 이름이 중요하지 않지만(이것은 생략될수도 있다.) 현재의 애플리케이션 컨텍스트내 모든 적절한 pointcut를 가져올것이다. 이 경우 TransactionAttributeSourceAdvisor타압의 "transactionAdvisor" bean정의는 클래스나 트랜잭션 속성을 운반하는 메소드에 적용할것이다. TransactionAttributeSourceAdvisor는 생성자 의존성을 통해 TransactionInterceptor에 의존한다. 예제는 autowiring을 통해 이것을 해결한다. AttributesTransactionAttributeSourceorg.springframework.metadata.Attributes인터페이스의 구현물에 의존한다. 이 잔해에서 "attributes" bean은 속성정보를 얻기 위해 Jakarta Commons Attributes API 사용하는 것을 만족한다. (애플리케이션 코드는 Commons Attributes 집계작업을 사용하여 컴파일되어야만 한다.)

JPetStore샘플 애플리케이션의 /annotation디렉토리는 JDK 1.5+ annotation이 다루는 자동프록시(auto-proxying)를 위한 유사한 예제를 포함한다. 다음 설정은 bean이 annotation을 포함하기 위한 프록시를 무조건 이끌어내는 Spring의 Transactional annotation의 자동 감지를 가능하게 한다.

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
  <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>

<bean id="transactionInterceptor"
    class="org.springframework.transaction.interceptor.TransactionInterceptor">
  <property name="transactionManager" ref="transactionManager"/>
  <property name="transactionAttributeSource">
    <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
  </property>
</bean>

여기에 정의된 TransactionInterceptor는 이것은 애플리케이션의 트랜잭션 요구사항(전형적으로 예제에서 처럼 JTA, 또는 Hibernate, JDO, JDBC)에 명시될 것이기 때문에 이 일반적인 파일내 포함되지 않는 PlatformTransactionManager정의에 의존한다.

<bean id="transactionManager" 
    class="org.springframework.transaction.jta.JtaTransactionManager"/>
[Tip]Tip

만약 당신이 선언적인 트랜잭션 관리만을 요구한다면 이러한 일반적인 XML정의를 사용하는 것은 Spring내에서 트랜잭션 속성을 가진 모든 클래스나 메소드를 자동적으로 프록싱하는 결과를 낳는다. 당신은 AOP로 직접적으로 작업을 하는것을 필요로 하지 않을것이고 프로그래밍 모델은 .NET ServicedComponents의 그갓과 유사하다.

이 기법은 확장가능하다. 사용자정의 속성에 기반하여 자동프록싱을 하는것은 가능하다. 당신이 다음 사항을 할 필요가 있다.

  • 당신의 사용자 지정 속성 명시하기.

  • 클래스나 메소드의 사용자정의 속성의 존재에 의해 처리되는 pointcut를 포함하는 필요한 advice를 가진 advisor명시하기. 당신은 사용자 지정 속성을 가져오는 정적 pointcut를 거의 구현하는 존재하는 advice를 사용하는것이 가능할지도 모른다.

각각의 advised클래스에 유일하기 위한 그러한 advisor(예를 들면 mixin)은 가능하다. 그것들은 싱글톤, bean정의보다는 프로토타입처럼 간단하게 정의될 필요가 있다. 예를 들어 위에서 보여진 Spring 테스트 슈트로부터의 LockMixin 소개(introduction) 인터셉터는 여기서 보여지는 것처럼 목표 mixin에 속성-지향 pointcut이 결합되어 사용되는 것이 가능하다. 우리는 자바빈 프라퍼티를 사용하여 설정된 포괄적인 DefaultPointcutAdvisor을 사용한다.

<bean id="lockMixin" class="org.springframework.aop.LockMixin"
    scope="prototype"/>

<bean id="lockableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
    scope="prototype">
  <property name="pointcut" ref="myAttributeAwarePointcut"/>
  <property name="advice" ref="lockMixin"/>
</bean>

<bean id="anyBean" class="anyclass" ...

만약 속성 인식 pointcut이 anyBean이나 다른 bean정의내 어떠한 메소드와 대응된다면 mixin은 적용될것이다. lockMixinlockableAdvisor은 프로토타입이라는것에 주의하라. myAttributeAwarePointcut pointcut은 개별적인 advised객체를 위한 상태를 유지하지 않는 것처럼 싱글톤 정의가 될수 있다.

7.10. TargetSources 사용하기

Spring은 org.springframework.aop.TargetSource인터페이스내에서 표현되는 TargetSource의 개념을 제공한다. 이 인터페이스는 joinpoint를 구현하는 "대상 객체(target object)"를 반환하는 책임을 가진다. TargetSource구현은 AOP프록시가 메소드 호출을 다루는 시점마다 대상 인스턴스를 요청한다.

Spring AOP를 사용하는 개발자는 대개 TargetSources를 직접적으로 작업할 필요가 없다. 하지만 이것은 풀링, 핫 스왑 그리고 다른 정교한 대상을 지원하는 강력한 방법을 제공한다. 예를 들면 풀링 TargetSource는 인스턴스를 관리하기 위한 풀을 사용하여 각각의 호출을 위한 다른 대상 인스턴스를 반환할수 있다.

만약 당신이 TargetSource을 명시하지 않는다면 디폴트 구현물은 로컬 객체를 포장하는것이 사용된다. 같은 대상은 (당신이 기대하는것처럼) 각각의 호출을 위해 반환된다.

Spring에 의해 제공되는 표준적인 대상 소스를 보자. 그리고 당신이 그것들을 어떻게 사용할수 있는지도 보자.

[Tip]Tip

사용자 지정 대상 소스를 사용할때 당신의 대상은 싱글톤 bean정의보다 프로토타입이 될 필요가 있을것이다. 이것은 요구될때 Spring이 새로운 대상 인스턴스를 생성하는것을 허용한다.

7.10.1. 핫 스왑가능한 대상 소스

org.springframework.aop.target.HotSwappableTargetSource는 이것에 대한 참조를 유지하기 위한 호출자를 허용하는 동안 교체되기 위한 AOP프록시의 대상을 허용하기 위해 존재한다.

대상 소스의 대상을 변경하는 것은 즉시 영향을 끼친다. HotSwappableTargetSource는 쓰레드에 안전하다(threadsafe).

당신은 다음처럼 HotSwappableTargetSource의 swap()메소드를 통해 대상을 변경할수 있다.

HotSwappableTargetSource swapper = 
    (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);

요구되는 XML정의는 다음처럼 볼수 있다..

<bean id="initialTarget" class="mycompany.OldTarget"/>

<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
  <constructor-arg ref="initialTarget"/>
</bean>

<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="targetSource" ref="swapper"/>
</bean>

위의 swap() 호출은 스왑가능한 bean의 대상을 변경한다. 그 bean에 대한 참조를 유지하는 클라이언트는 변경을 인식하지 못할것이지만 새로운 대상에 즉시 도달할것이다.

비록 이 예제는 어떠한 advice를 추가하지 않고 TargetSource를 사용하기 위한 advice를 추가할 필요가 없다. 물론 어떤 TargetSource는 임의의 advice로 결합하여 사용될수 있다.

7.10.2. 풀링 대상 소스

풀링 대상 소스를 사용하는것은 일치하는 인스턴스의 풀이 메소드 호출로 풀내 객체가 자유롭게 되는 방식으로 유지되는 비상태유지(stateless) 세션 EJB와 유사한 프로그래밍 모델을 제공한다.

Spring풀링과 SLSB풀링 사이의 결정적인 차이점은 Spring풀링은 어떠한 POJO에도 적용될수 있다는 것이다. 대개 Spring을 사용하는 것은 이 서비스가 비-침락젹인 방법으로 적용될수 있다.

Spring은 상당히 효과적인 풀링 구현물을 제공하는 Jakarta Commons Pool 1.3을 위한 특별한 지원을 제공한다. 당신은 이 기능을 사용하기 위해 애플리케이션 클래스패스내 commons-pool.jar파일이 필요할것이다. 이것은 다른 풀링 API를 지원하기 위해 org.springframework.aop.target.AbstractPoolingTargetSource의 하위클래스를 구현하는 것이 가능하다.

샘플 설정은 아래에서 보여진다.

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" 
    scope="prototype">
  ... properties omitted
</bean>

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
  <property name="targetBeanName" value="businessObjectTarget"/>
  <property name="maxSize" value="25"/>
</bean>

<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="targetSource" ref="poolTargetSource"/>
  <property name="interceptorNames" value="myInterceptor"/>
</bean>

예제내 대상 객체인 "businessObjectTarget"이 프로토타입이 되어야만 한다는 것에 주의하라. 이것은 PoolingTargetSource구현물이 필요할때 풀의 증가를 위한 대상의 새로운 인스턴스를 생성하는것을 허용한다. 이것의 프라퍼티에 대한 정보를 위해 사용하기 위한 AbstractPoolingTargetSource와 견고한 하위클래스를 위한 JavaDoc를 보라. maxSize는 가장 기본적이고 표현되기 위해 항상 보증된다.

이 경우 "myInterceptor"는 같은 IoC컨텍스트내 정의될 필요가 있는 인터셉터의 이름이다. 이것은 풀링을 사용하기 위해 인터셉터를 명시할 필요가 없다. 만약 당신이 오직 풀링만을 원하고 다른 advice는 원하지 않는다면 interceptorNames 프라퍼티를 전부 셋팅하지 말라.

소개(introduction)을 통해 풀의 설정과 현재 크기에 대한 정보를 드러내는 org.springframework.aop.target.PoolingConfig인터페이스를 위한 어떤 풀링된 객체를 형변환하는것을 가능하게 하는것처럼 Spring을 설정하는것은 가능하다. 당신은 이것처럼 advisor을 명시할 필요가 있을것이다.

<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="targetObject" ref="poolTargetSource"/>
  <property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>

이 advisor는 AbstractPoolingTargetSource클래스의 편리한 메소드를 호출하고 나아가 MethodInvokingFactoryBean의 사용하여 얻을수 있다. 이 advisor의 이름(여기 "poolConfigAdvisor")은 풀링된 객체를 드러내는 ProxyFactoryBean내 인터셉터 이름의 리스트이 될수 있다.

형변환은 다음처럼 보일것이다.

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
[Note]Note

풀링 비상태유지(stateless) 서비스 객체는 언제나 필요한것은 아니다. 우리는 이것이 대부분의 비상태유지(stateless) 객체가 근본적으로 쓰레드에 안전하고 인스턴스 풀링은 자원이 캐시된다면 골치거리가 되는것처럼 디폴트 선택이 될것이라고 믿지는 않는다.

좀더 간단한 풀링은 자동프록싱을 사용하여 사용가능하다. 어떤 autoproxy생성자에 의해 사용될수 있는 TargetSources 셋팅은 가능하다.

7.10.3. 프로토 타입 대상 소스

"프로토타입" 대상 소스를 셋업하는 것은 풀링 TargetSource와 유사하다. 이 경우 대상의 새로운 인스턴스는 모든 메소드호출에서 생성될것이다. 비록 새로운 객체를 생성하는 비용이 요즘 JVM내에서는 높지않더라도 새로운 객체(IoC의존성을 만족하는)를 묶는 비용은 좀더 비쌀것이다. 게다가 당신은 매우좋은 이유없이 이 접근법을 사용하지 않을것이다.

이것을 하기 위해 당신은 다음처럼 위에서 보여진 poolTargetSource정의를 변경할수 있다. (나는 명백하게 하기 위해 이름을 변경했다.)

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
  <property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

여기엔 오직 하나의 프라퍼티(대상 빈의 이름)가 있다. 상속은 일관적인 명명을 확실히 하기 위한 TargetSource구현물내 사용되었다. 풀링 대상 소스를 사용하는 것처럼 대상 bean은 프로토타입 bean정의가 되어야만 한다.

7.10.4. ThreadLocal 대상 소스

ThreadLocal 대상 소스는 만약 당신이 들어오는 각각의 요청(쓰레드마다)을 위해 생성되기 위한 객체가 필요하다면 유용하다. ThreadLocal의 개념은 쓰레드와 함께 자원을 투명하게 저장하기 위한 JDK범위의 기능을 제공한다. ThreadLocalTargetSource를 셋업하는 것은 다른 대상 소스를 위해 설명되는 것과 거의 같다.

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
  <property name="targetBeanName" value="businessObjectTarget"/>
</bean>
[Note]Note

ThreadLocals은 멀티-쓰레드와 멀티-클래스로더 환경내 그것들을 정확하게 사용하지 않았을때 다양한 문제(잠재적으로 메모리 누수와 같은 결과)를 발생시킨다. 하나는 몇몇 다른 클래스로 threadlocal를 포장하는 것을 언제나 검토해야만 하고 ThreadLocal자체(물론 래퍼클래스를 제외하고)를 결코 직접적으로 사용하지 말라. 또한 하나는 쓰레드를 위한 로컬 자원을 정확하게 셋팅하고 셋팅하지 않는 것을(후차는 ThreadLocal.set(null)에 대한 호출을 간단하게 포함한다.) 언제나 기억해야만 한다. 셋팅하지 않는것은 문제가 되는 행위를 야기하는 셋팅을 하기 때문에 이 경우 수행될수 있다. Spring의 ThreadLocal지원은 당신을 위해 이것을 하고 다른 임의의 핸들링 코드없이 ThreadLocals를 사용하여 검토되어야만 한다.

7.11. 새로운 Advice 타입을 정의하기

Spring AOP는 확장가능하기 위해 디자인되었다. 인터셉션 구현물 전략이 내부적으로 사용되는 동안 특별히 지원되는 임의의 advice타입에 추가적으로 인터셉션 around advice, before, throw advice그리고 after returning advice를 지원하는 것이 가능하다.

org.springframework.aop.framework.adapter패키지는 핵심 프레임워크 변경없이 추가되기 위한 사용자 지정 advice타입을 위한 지원을 허용하는 SPI패키지이다. 사용자 지정 Advice타입의 제한은 org.aopalliance.aop.Advice태그 인터페이스를 구현해야만 한다는 것이다.

더 많은 정보를 위해서 org.springframework.aop.framework.adapter패키지의 JavaDoc를 참조하라.

7.12. 추가적인 자원

Spring AOP의 좀더 다양한 예제를 위해 Spring샘플 애플리케이션을 참조하라.

  • JPetStore의 디폴트 설정은 선언적인 트랜잭션 관리를 위한 TransactionProxyFactoryBean의 사용을 설명한다.

  • JPetStore의 /attributes 디렉토리는 속성-지향 선언적인 트랜잭션 관리의 사용을 설명한다.