[FRAMEWORK] Spring AOP: Spring을 이용한 Aspect 지향

Chapter 5. Spring AOP: Spring을 이용한 Aspect 지향적인 프로그래밍
5.1. 개념
Aspect-지향 프로그래밍 (AOP)는 프로그램 구조에 대한 다른 방식의 생각을 제공함으로써 OOP를 보완한다. OO가 애플리케이션을 객체구조로 분석하는 동안 AOP는 프로그램을 aspects 나 관심사(concerns)로 분석한다. 이것은 다중 객체로부터 공통적으로 잘라낼수 있는 트랜잭션 관리와 같은 관심사의 모듈화(이러한 관심사는 종종 crosscutting 관심사라는 용어로 사용된다.) 를 가능하게 한다.

Spring의 핵심이 되는 컴포넌트중 하나는 AOP 프레임워크이다. Spring IoC컨테이너(BeanFactory와 ApplicationContext) 가 AOP에 의존하지 않는 동안 당신이 원하지 않는다면 AOP를 사용할 필요가 없다는 것을 의미한다. AOP는 미들웨어 솔루션의 기능을 제공하기 위해 Spring IoC를 보완할것이다.

AOP는 Spring내에서 사용된다.

선언적인 기업용 서비스를 제공하기 위해 EJB 선언적 서비스를 위한 대체물처럼 사용될수 있다. 서비스처럼 가장 중요한 것은 Spring의 트랜잭션 추상화에서 빌드되는 선언적인 트랜잭션 관리이다.

사용자 정의 aspect를 구현하는 것을 사용자에게 허용하기 위해 AOP를 사용하여 OOP의 사용을 기능적으로 보완한다.

게다가 당신은 EJB없이 선언적인 트랜잭션 관리를 제공하는 것을 Spring에 허용하도록 하는 기술을 가능하게 하는것처럼 Spring AOP를 볼수 있다. 또는 사용자 지정 aspect를 구현하기 위한 Spring AOP프레임워크의 강력한 힘을 사용할수 있다.

만약 당신이 일반적인 선언적 서비스나 풀링과 같은 다른 미리 패키징된 선언적 미들웨어 서비스만 관심을 가진다면 당신은 Spring AOP를 사용하여 직접적으로 작업할 필요가 없다. 그리고 이 장의 대부분을 그냥 넘어갈수 있다.
5.1.1. AOP 개념
몇몇 중심적인 AOP개념을 명시함으로써 시작해보자. 이 개념들은 Spring에 종속적인 개념이 아니다. 운 나쁘게도 AOP전문용어는 특히 직관적이지 않다. 어쨌든 Spring이 그 자신의 전문용어를 사용했다면 좀더 혼란스러울것이다.

Aspect: 구현물이 다중 객체를 짤라내는 것을 위한 concern의 모듈화. 트랜잭션 관리는 J2EE애플리케이션의 crosscutting concern의 좋은 예제이다. aspect는 advisor이나 인터셉터처럼 Spring을 사용하여 구현된다.

Joinpoint: 메소드 호출이나 특정 예외를 던지는 것과 같은 프로그램을 수행하는 지점. Spring AOP에서 joinpoint는 언제나 메소드 호출이다. Spring은 두드러지게 joinpoint개념을 사용하지는 않는다. joinpoint정보는 인터셉터로 인자를 전달하는 MethodInvocation의 메소드를 통해 접근가능하다. 그리고 org.springframework.aop.Pointcut인터페이스의 구현물에 의해 평가된다.

Advice: 특정 joinpoint에서 AOP프레임워크에 의해 획득되는 액션. advice의 각각의 타입은 "around," "before" 과 "throws"를 포함한다. advice 타입은 밑에서 언급된다. Spring을 포함한 많은 AOP프레임워크는 인터셉터처럼 advice를 모델화하고 joinpoint "주위"로 인터셉터의 묶음(chain)을 유지한다.

Pointcut: advice가 수행될때를 명시하는 joinpoint의 모음. AOP프레임워크는 개발자에게 예를 들면 정규식 표현을 사용하는 것과 같은 pointcut를 명시하도록 허용해야 한다.

Introduction: advised 클래스에 메소드나 필드추가하기. Spring은 어떠한 advised클래스에 새로운 인터페이스를 소개(introduce)하는것을 허용한다. 예를 들면 당신은 간단한 캐싱을 위해 IsModified인터페이스를 구현한 어떠한 객체를 만들기 위해 introduction을 사용할수 있다.

대상 객체: 객체는 joinpoint를 포함한다. 또한 advised 나 proxied객체를 참조한다.

AOP 프록시: AOP프레임워크에 의해 생성되는 advice를 포함한 객체. Spring에서 AOP프록시는 JDK동적 프록시나 CGLIB프록시가 될것이다.

Weaving: advised객체를 생성하기 위한 aspect 조합. 이것은 컴파일(예를 들면 AspectJ컴파일러를 사용하는) 시각이나 수행시각에 수행될수 있다. 다른 순수한 자바 AOP프레임워크처럼 Spring은 수행시에 작성된다.

포함되는 다양한 타입의 advice

Around advice: 메소드 호출과 같은 joinpoint주위(surround)의 advice. 이것은 가장 강력한 종류의 advice이다. Around advice는 메소드 호출 전후에 사용자 지정 행위를 수행한다. 그것들은 joinpoint를 처리하거나 자기 자신의 반환값을 반환함으로써 짧게 수행하거나 예외를 던지는 것인지에 대해 책임을 진다.

Before advice: joinpoint전에 수행되는 advice. 하지만 joinpoint를 위한 수행 흐름 처리(execution flow proceeding)를 막기위한 능력(만약 예외를 던지지 않는다면)을 가지지는 않는다.

Throws advice: 메소드가 예외를 던질다면 수행될 advice. Spring은 강력한 타입의 Throws advice를 제공한다. 그래서 당신은 Throwable 나 Exception으로 부터 형변환 할 필요가 없는 관심가는 예외(그리고 하위클래스)를 잡는 코드를 쓸수 있다.

After returning advice: joinpoint이 일반적으로 예를 들어 메소드가 예외를 던지는것없이 반환된다면 완성된 후에 수행되는 advice.

Around advice는 가장 일반적인 종류의 advice이다. Nanning Aspects와 같은 대부분의 인터셉션-기반의 AOP프레임워크는 오직 around advice만을 제공한다.

AspectJ처럼 Spring이 advice타입의 모든 범위를 제공하기 때문에 우리는 요구되는 행위를 구현할수 있는 최소한의 강력한 advice타입을 사용하길 권한다. 예를 들어 당신이 메소드의 값을 반환하는 캐시만을 수정할 필요가 있다면 around advice가 같은것을 수행할수 있다고하더라도 around advice보다 advice를 반환한 후에 구현하는게 더 좋다. 대부분 특정 advice타입을 사용하는것은 잠재적으로 적은 에러를 가지는 간단한 프로그래밍 모델을 제공한다. 예를 들어 당신은 around advice를 위해 사용되는 MethodInvocation의 proceed()메소드를 호출할 필요가 없고 나아가 그것을 호출하는것을 실패할수도 있다.

pointcut 개념은 인터셉션(interception)을 제공하는 오래된 기술로 부터 AOP를 구별하는 수단이 되는 AOP의 핵심이다. pointcut는 OO구조가 비의존적으로 대상화되기 위한 advice를 가능하게 한다. 예를 들면 선언적인 트랜잭션을 제공하는 around advice는 다중 객체에 걸쳐있는 메소드 모음에 적용될수 있다. 게다가 pointcut는 AOP의 구조적인 요소를 제공한다.

5.1.2. Spring AOP의 기능과 대상
Spring AOP는 순수자바로 구현되었다. 특별한 편집 절차가 필요하지 않다. Spring AOP는 클래스로더 구조를 제어할 필요가 없고 J2EE웹 컨테이너나 애플리케이션 서버내 사용되는것이 적합하다.

Spring은 현재 메소드 호출의 인터셉션(interception)을 지원한다. 필드 인터셉션은 비록 필드인터셉션이 핵심 Spring AOP API에 영향없이 추가될수 있지만 구현되지 않았다.

필드 인터셉션은 어쩌면 OO캡슐화를 침범한다. 우리는 이것이 애플리케이션 개발에서 현명하다고 생각하지 않는다. 만약 당신이 필드 인터셉션을 요구한다면 AspectJ를 사용하는것을 생각해보라.
Spring은 pointcut과 다른 advice타입을 표현하기 위한 클래스를 제공한다. Spring은 aspect를 표현하는 객체를 위해 advisor라는 개념을 사용하고 joinpoint를 명시하기 위해 이것을 대상으로하는 advice와 pointcut모두를 포함한다.

다른 advice타입은 (AOP제휴(Alliance) 인터셉션 API로 부터)MethodInterceptor이고 advice인터페이스는 org.springframework.aop패키지내 정의된다. 모든 advice는 org.aopalliance.aop.Advice태그 인터페이스를 구현해야만 한다. advice는 MethodInterceptor; ThrowsAdvice; BeforeAdvice; 그리고 AfterReturningAdvice를 지원한다. 우리는 밑에서 advice타입에 대해서 상세하게 언급할것이다.

Spring은 AOP 제휴(Alliance)인터셉션 인터페이스(http://www.sourceforge.net/projects/aopalliance)를 구현한다. around advice는 AOP제휴 org.aopalliance.intercept.MethodInterceptor인터페이스를 구현해야만 한다. 이 인터페이스의 구현은 Spring내에서 뿐 아니라 다른 어떠한 AOP제휴 호환 구현물에서도 작동할수 있다. 현재 JAC는 AOP제휴 인터페이스를 구현하고 Nanning 과 Dynaop는 2004년 초 구현할 가능성이 있다.

AOP에 대한 Spring의 접근법은 대부분의 다른 AOP프레임워크와 다르다. 그 목적은 Spring AOP가 대부분 구현가능하더라도 완벽한 AOP구현물을 제공하기 않는것이다. 이것은 기업용 애플리케이션내 공통적인 문제점 해결을 돕기위해 AOP구현물과 Spring IoC사이의 닫힌(close) 통합을 제공하기 위해서이다.

게다가 예를 들어 Spring의 AOP기능은 Spring IoC컨테이너와 협력하여 작동된다. AOP advice는 비록 이것이 강력한 "autoproxying"기능을 허용한다고 할지라도 일반적인 빈 정의 문법을 사용해서 명시된다. advice와 pointcut는 Spring IoC에 의해 스스로 관리되는데 이것이 다른 AOP구현물과의 결정적인 차이점이다. Spring AOP를 사용하여 매우 잘 정리된 객체를 권하는(advise) 것처럼 당신이 쉽게 또는 효율적으로 할수 없는 것들이 있다. AspectJ는 아마도 이러한 경우에 최고의 선택이다. 어쨌든 우리의 경험은 Spring AOP가 AOP를 다루는 J2EE애플리케이션내 대부분의 문제를 위한 멋진 솔루션를 제공한다는 것이다.

Spring AOP는 복잡한 AOP솔루션을 제공하기 위해 AspectJ 나 AspectWerkz에 대해 침범하지 않을것이다. 우리는 Spring과 같은 프록시 기반 프레임워크와 AspectJ와 같은 성숙한 프레임워크 모두 가치있고 경쟁보다는 서로 보완할수 있다고 믿는다. 게다가 Spring 1.1의 가장 큰 우선사항은 일관적인 Spring기반의 애플리케이션 구조내 응답되는 위한 AOP의 모든 사용을 가능하게 하기 위해 Spring AOP와 AspectJ와의 IoC가 균일하게 통합되는 것이다. 이 통합은 Spring AOP API나 AOP제휴 API에 영향을 끼치지 않는다. Spring AOP는 호환적인 면만 남을것이다.

5.1.3. Spring 내 AOP 프록시
Spring은 AOP프록시를 위해 J2SE 동적 프록시(dynamic proxies)를 사용하는것이 디폴트이다. 이것은 프록시가 되기 위한 어떤 인터페이스나 인터페이스의 모음을 가능하게 한다.

Spring은 또한 CGLIB프록시도 사용가능하다. 이것은 인터페이스보다는 클래스를 프록시화 하기 위해 필요하다. CGLIB는 비지니스 객체가 인터페이스를 구현하지 않는다면 디폴트로 사용된다. 클래스보다는 인터페이스를 위한 프로그램을 위해 좋은 경험이기 때문에 비지니스 객체는 일반적으로 하나 이상의 비지니스 인터페이스를 구현할것이다.

이것은 강제로 CGLIB의 사용하도록 할수 있다. 우리는 이것을 밑에서 언급할것이다 당신이 왜 이렇게 하는것을 원하는지 설명할것이다.

Spring 1.0이후 Spring은 생성된 클래스들 전부를 포함하는 AOP프록시의 추가적인 타입을 제공한다. 이것은 프로그래밍 모델에 영향을 끼치지 않을것이다.
5.2. Spring내 Pointcuts
Spring이 중대한 pointcut개념을 어떻게 다루는지 보자.

5.2.1. 개념
Spring의 pointcut모델은 advice타입의 비의존성을 재사용한다. 이것은 같은 pointcut을 사용하여 다른 advice를 대상으로 하는것이 가능하다.

org.springframework.aop.Pointcut인터페이스는 특정 클래스와 메소드에 대해 대상 advice가 사용되는 중심적인 인터페이스이다. 완전한 인터페이스는 밑에서 보여준다.

public interface Pointcut {

ClassFilter getClassFilter();

MethodMatcher getMethodMatcher();

}
Pointcut인터페이스를 두개의 부분으로 쪼개는 것은 부분들에 적합한 클래스와 메소드, 그리고 잘 조직된 기능(다른 메소드 적합자(matcher)와의 "union"를 수행하는것과 같은)의 재사용을 허용한다.

ClassFilter인터페이스는 주어진 대상 클래스의 모음을 위해 pointcut를 제한하기 위해 사용된다. 만일 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);
}
matches(Method, Class) 메소드는 이 pointcut이 대상 클래스에서 주어진 메소드에 적합할지 테스트하기 위해 사용된다. 이 평가는 모든 메소드 호출에서 테스트를 위한 필요성을 제거하기 위해 AOP프록시가 생성되었을때 수행될수 있다. 만약 두개의 인자가 적합한 메소드가 주어진 메소드를 위해 true를 반환한다면 MethodMatcher를 위한 isRuntime()메소드는 true를 반환한다. 세개의 인자가 적합한 메소드는 모든 메소드 호출에서 호출될것이다. 이것은 대상 advice가 수행되기 전에 즉시 메소드 호출로 전달되는 인자를 보기 위한 pointcut을 가능하게 한다.

대부분의 MethodMatchers는 그들의 isRuntime()메소드가 false를 반환하는 의미에서 정적이다. 이 경우 세개의 인자가 적합한 메소드는 결코 호출되지 않을것이다.

가능하다면 pointcut을 AOP프록시가 생성될때 pointcut의 평가결과를 캐시하기 위한 AOP프레임워크를 허용하도록 정적으로 만들도록 시도하라.
5.2.2. pointcuts에서의 작업(operation)
Spring은 pointcut에서의 작업(union 과 intersection)을 지원한다.

Union은 어느한쪽의 pointcut가 들어맞는 방식을 의미한다.

Intersection은 pointcut둘다 들어맞는 방식을 의미한다.

Union이 언제나 좀더 유용하다.

Pointcuts는 org.springframework.aop.support.Pointcuts클래스내 정적 메소드를 사용하거나 같은 패키지내 ComposablePointcut클래스를 사용하여 구성될수 있다.

5.2.3. 편리한 pointcut 구현물
Spring은 다양하고 편리한 pointcut구현물을 제공한다. 몇몇은 특별히 사용될수 있다. 다른것은 애플리케이션에 종속적인 pointcut내에서 하위클래스화 되는 경향이 있다.

5.2.3.1. 정적 pointcuts
정적 pointcut는 메소드와 대상 클래스에 기반하고 메소드의 인자를 고려할수는 없다. 정적 pointcut는 대부분의 사용상황을 위해 충분하고 가장 좋다. Spring이 메소드가 처음 호출될때 정적 pointcut를 한번만 평가하는것은 가능하다. 그 후 각각의 메소드 호출시 pointcut를 다시 평가하는것은 필요하지 않다.

Spring에 포함된 몇몇 정적 pointcut구현물을 생각해보자.

5.2.3.1.1. 정규표현식 pointcuts
정적 pointcut를 명시하는 하나의 분명한 방법은 정규표현식이다. Spring외 다양한 AOP프레임워크는 이것이 가능하다. org.springframework.aop.support.RegexpMethodPointcut는 Perl 5 정규표현식 문법을 사용하는 일반적인 정규표현식 pointcut이다.

이 클래스를 사용하면 당신은 문자열 형태의 목록을 제공할수 있다. 만약 이러한 것들이 들어맞는다면 pointcut는 true로 평가할것이다. (그래서 이 결과는 이러한 pointcut의 표과적인 union이다.)

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

[bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.RegexpMethodPointcut"]
[property name="patterns"]
[list]
[value].*get.*[/value]
[value].*absquatulate[/value]
[/list]
[/property]
[/bean]
RegexpMethodPointcut, RegexpMethodPointcutAdvisor의 편리한 하위클래스는 우리에게 advice또한 참조하도록 허락한다.(advice는 advice나 advice를 던지는등 이전에 인터셉터될수 있다는 것을 기억하라.) 이것은 하나의 빈이 pointcut와 advisor모두를 제공하거나 아래의 사항처럼 배선(wiring)을 단순화한다.

[bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"]
[property name="advice"]
[ref local="beanNameOfAopAllianceInterceptor"/]
[/property]
[property name="patterns"]
[list]
[value].*get.*[/value]
[value].*absquatulate[/value]
[/list]
[/property]
[/bean]
RegexpMethodPointcutAdvisor는 어떠한 advice타입과도 사용될수 있다.

RegexpMethodPointcut클래스는 Jakarta ORO정규 표현식 패키지를 요구한다.
5.2.3.1.2. 속성지향(Attribute-driven) pointcuts
정적 pointcut의 중요한 타입은 metadata-driven pointcut이다. 이것은 메타데이타(전형적으로 소세레벨의 메타데이타) 속성들의 값을 사용한다.

5.2.3.2. 동적 pointcuts
동적 pointcut은 정적 pointcut보다 평가하는것이 손실이 크다(costlier). 그것들은 정적 정보만큼 메소드 arguments를 고려한다. 이것은 모든 메소드 호출에서 그들이 평가가 되어야 함을 의미한다. 결과는 인자가 다양하기 때문에 캐시될수 없다.

중요한 예제는 흐름 제어(control flow) pointcut 이다.

5.2.3.2.1. 흐름 제어(Control flow) pointcuts
Spring 흐름 제어 pointcut는 비록 덜 강력하지만 개념적으로 AspectJ cflow pointcut와 흡사하다. (현재 밑의 다른 pointcut를 수행하는 pointcut을 명시하는 방법은 없다.). 흐름 제어 pointcut는 최근의 호출 스택에 대응된다. 예를 들면 이것은 com.mycompany.web패키지내 메소드나 SomeCaller클래스에 의해 joinpoint가 호출된다면 실행한다. 흐름 제어 pointcut는 org.springframework.aop.support.ControlFlowPointcut클래스를 사용하여 몇시된다.

Note
흐름 제어 pointcut는 다른 동적 pointcut보다 수행시 평가되기 위해 명백하게 좀더 비싸다. Java1.4에서 다른 동적 pointcut의 5배이고 Java1.3에서는 10배이상이다.


5.2.4. Pointcut 수퍼클래스(superclasses)
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를 사용할수 있다.

5.2.5. 사용자지정 pointcuts
Spring내 pointcut가 언어적(AspectJ처럼)인 면보다 자바클래스이기 때문에 이것은 정적이든 동적이든 사용자지정 pointcut를 선언하는것이 가능하다. 어쨌든 AspectJ문법내 작성될수 있는 정교한 pointcut표현식을 위해 특별한 지원은 없다. 어쨌든 Spring내 사용자 지정 pointcut는 임의로 복잡하게 될수 있다.

Spring의 가장 최근버전은 아마 JAC에 의해 제공되는 것처럼 "의미적인(semantic) pointcut"를 위한 지원이 제공될지도 모른다. 예를 들면 "대상 객체내에서 인스턴스 변수를 변경하는 모든 메소드"
5.3. Spring 내 Advice타입들
Spring AOP가 advice를 어떻게 다루는지에 대해서 지금 보자.

5.3.1. Advice 생명주기
Spring advices는 모든 adviced객체를 통해 공유되거나 각각의 advised객체에 대해 유일할 수 있다. 이것은 per-class 나 per-instance advice에 일치한다.

Per-class advice는 매우 종종 사용된다. 이것은 트랜잭션 advisor처럼 일반적인 advice를 선호한다. 그것들은 프록시된 객체의 상태에 의존하지 않고 새로운 상태를 추가한다. 그것들은 단지 메소드와 인자에 옇양을 준다.

Per-instance advice는 도입이나 mixin을 지원하기 위해 선호한다. 이 경우 advice는 프록시된 객체에 상태를 추가한다.

이것은 같은 AOP프록시내에서 공유되는것들의 mixin과 per-instance advice를 사용하는것이 가능하다.

5.3.2. Spring내 Advice 타입들
Spring은 다양하고 특별한 advice타입들을 제공한다. 그리고 독단적인 advice타입을 지원하기 위해 확장가능하다. 기본적인 개념과 표준적인 advice타입을 보자.

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

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

public interface MethodInterceptor extends Interceptor {

Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()메소드를 위한 MethodInvocation인자는 호출될 메소드(대상 joinpoint; AOP프록시; 그리고 메소드를 위한 인자)를 드러낸다. invoke()메소드는 호출 결과(joinpoint의 반환값)를 반환한다.

간단한 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()메소드를 호출하는것에 주의하라. 이것은 joinpoint쪽으로 인터셉터 연계작업(chain)을 처리한다. 대부분의 인터셉터는 이 메소드를 호출할것이고 그 자신의 반환값을 반환한다. 어쨌든 어떠한 around advice와 같은 MethodInterceptor는 처리 메소드를 호출하는 것보다 다른 값을 반환하거나 예외를 던질수 있다. 어쨌든 당신은 좋은 이유없이는 이것을 원하지 않을것이다.

MethodInterceptors는 다른 AOP제휴 AOP구현물과 상호작용성을 제공한다. 다른 advice타입은 공통적인 AOP개념을 구현하는 이 부분의 나머지에서 Spring 특유의 방법으로 언급된다. 대부분의 특정 advice타입을 사용하는 방법으로 장점을 가지는 동안 만약 당신이 다른 AOP프레임워크내 aspect를 수행하길 원한다면 MethodInterceptor around advice에 충실하라. 현재 pointcut는 프레임워크 사이에 상호작용가능하지 않고 AOP제휴는 현재 pointcut인터페이스를 정의하지 않는다.
5.3.2.2. 전(Before) advice
좀더 간단한 advice타입은 전(before) advice이다. 이것은 오직 메소드에 들어가지전에 호출되기 때문에 MethodInvocation객체를 필요로 하지 않는다.

전(before) advice의 가장 중요한 장점은 proceed() 메소드를 호출할 필요가 없다는 것이다. 그리고 인터셉터 연계작업을 처리하는 것을 무심코 실패하는 가능성이 없다.

MethodBeforeAdvice 인터페이스는 아래에서 보여진다. (Spring API디자인은 비록 평상시 객체가 필드 인터셉션에 적용하고 Spring이 이것을 구현할 가능성은 없음에도 불구하고 advice앞의 필드를 허용한다.).

public interface MethodBeforeAdvice extends BeforeAdvice {

void before(Method m, Object[] args, Object target) throws Throwable;
}
반환타입은 void라는것에 주의하라. 전(before) advice는 joinpoint가 수행되기 전에 사용자 지정 행위를 추가할수 있다. 하지만 반환값을 변경할수는 없다. 만약 전(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;
}
}
전(before) advice 는 어떠한 pointcout와 사용될수 있다.
5.3.2.3. Throws advice
Throws advice는 joinpoint가 예외를 던진다면 joinpoint를 반환한후에 호출된다. Spring은 throws advice타입을 제공한다. 이것은 org.springframework.aop.ThrowsAdvice인터페이스가 어떠한 메소드도 포함하지 않는다는 것을 의미한다. 이것은 주어진 객체가 하나 이상의 throws advice타입의 메소드를 구현하는 것을 표시하는 태그 인터페이스이다. 그것들은 폼의 형태가 될것이다.

afterThrowing([Method], [args], [target], subclassOfThrowable)
오직 마지막의 인자만이 요구된다. 게다가 한개에서 네개까지의 인자는 advice메소드가 메소드와 인자에 연관되는지에 의존한다. 다음은 throws advice의 예제이다.

이 advice는 RemoteException과 하위 클래스가 던져진다면 호출될것이다.

public class RemoteThrowsAdvice implements ThrowsAdvice {

public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
다음의 advice는 ServletException가 던져진다면 호출된다. 위 advice와는 다르게 이것은 네개의 인자를 명시한다. 그래서 이것은 메소드 인자와 대상 객체로 호출되는 메소드에 접근한다.

public static class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something will all arguments
}
}
마지막 예제는 두개의 메소드가 RemoteException 과 ServletException 둘다 다루는 하나의 클래스내에서 어떻게 사용되는지 설명한다. 많은 수의 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
}
}
Throws advice 어떠한 pointcut와 사용될수 있다.
5.3.2.4. advice를 반환한 후(after returning advice)
Spring내에서 advice를 반환한 후(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 returing 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는 실행경로를 변경하지 않는다. 만약 이것이 예외를 던진다면 이것은 반환값 대신에 인터셉터 연계를 던질것이다.

After returning advice는 어떠한 pointcut도 함께 사용될수 있다.
5.3.2.5. Introduction advice
Spring은 특별한 종류의 interception advice처럼 introduction advice를 처리한다.

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

public interface IntroductionInterceptor extends MethodInterceptor {

boolean implementsInterface(Class intf);
}
AOP제휴 MethodInterceptor 인터페이스로 부터 상속된 invoke() 메소드는 introduction을 구현해야만 한다. 만약 호출된 메소드가 introduced인터페이스에 있다면 introduction인터셉터는 메소드 호출을 다룰 책임을 가지고 있으며 이것은 proceed()을 호출할수 없다.

Introduction advice는 메소드보다는 클래스에만 적용하는 것처럼 어떠한 pointcut와 함께 사용될수 없다. 당신은 다음의 메소드를 가지는 InterceptionIntroductionAdvisor를 사용하여 introduction advice만을 사용할수 있다.

public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {

ClassFilter getClassFilter();

IntroductionInterceptor getIntroductionInterceptor();

Class[] getInterfaces();
}
introduction advice와 관련하여 MethodMatcher가 없고 나아가 Pointcut도 없다. 오직 클래스 필터링이 논리적이다.

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

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는 introduced인터페이스의 실질적인 구현을 위해 introduction을 위임하도록 디자인되었고 그렇게 하기 위한 인터셉션의 사용을 숨긴다. 그 위임은 생성자 인자를 사용해서 어떠한 객체를 위한 셋팅될수 있다. 디폴트(인자없는 생성자를 사용할 때) 위임이 이것이다. 게다가 이것은 아래의 예제에 있다. 위임은 DelegatingIntroductionInterceptor의 LockMixin 하위 클래스이다. 주어진 위임(디폴트에 의해)님 DelegatingIntroductionInterceptor인스턴스는 위임(IntroductionInterceptor이 아닌)에 의해 구현된 모든 인터페이스를 찾는다. 그리고 그것들에 대해 introduction을 지원할것이다. 인터페이스를 억제하기 위한 suppressInterflace(Class intf) 메소드를 호출하기 위해 LockMixin과 같은 하위클래스가 드러낼수(exposed) 없다. 어쨌든 많은 인터페이스 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()메소드를 오버라이드(메소드가 소개(introduced)되었다면 위임메소드를 호출하는 DelegatingIntroductionInterceptor구현) 할 필요가 없다. 반면에 joinpoint를 향한 처리는 언제나 충분하다. 현재 상태에서 우리는 locked상태라면 호출될수 있는 setter메소드가 없다는 것을 체크할 필요가 있다.

요구되는 introduction advisor는 간단하다. 이것을 하기 위해 필요한 모든것은 구별되는 LockMixin인스턴스를 유지하고 Lockable의 경우에서 소개된(introduced) 인터페이스를 명시하는것이다. 좀더 복잡한 예제는 introduction인터셉터(프로토타입처럼 명시된)에 대한 참조를 가져온다. 이 경우 LockMixin에 관련된 설정은 없다. 그래서 new를 사용해서 이것을 간단하게 생성한다.

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
우리는 매우 간단하게 이 advisor를 적용할수 있다. 이것은 설정을 요구하지 않는다. (어쨌든 이것은 필요하다. IntroductionAdvisor없이 IntroductionInterceptor을 사용하는 것은 불가능하다.) 늘 그렇듯이 introduction으로 advisor은 이것이 상태유지(stateful)인 것처럼 인스턴스당 하나가 되어야 한다. 우리는 각각의 advised객체를 위해 LockMixinAdvisor의 다른 인스턴스와 LockMixin이 필요하다. advisor는 advised객체의 상태의 일부로 구성된다.

우리는 프로그램에 따라 Advised.addAdvisor() 를 사용하거나 XML설정(추천되는 방식)으로 다른 advisor처럼 이 advisor를 적용할수 있다. 밑에서 언급되는 "자동 프록시 생성자"를 포함한 모든 프록시 생성 선택은 introduction과 상태유지 mixin을 다룬다.

5.4. Spring내 Advisors
Spring내 advisor는 aspect의 모듈이다. advisor는 전형적으로 advice와 pointcut를 결합한다.

introduction의 특별한 경우 이외에 어떤 advisor는 어떠한 advice와 사용될수 있다. org.springframework.aop.support.DefaultPointcutAdvisor는 가장 공통적으로 사용되는 advisor클래스이다. 예를 들면 이것은 MethodInterceptor, BeforeAdvice 또는 ThrowsAdvice과 사용될수 있다.

같은 AOP프록시로 Spring내에서 advisor과 advice타입들을 혼합하는 것은 가능하다. 예를 들면 당신은 하나의 프록시 설정으로 interception around advice, throws advice 그리고 before advice를 사용할수 있다. Spring은 자동적으로 필요한 생성 인터셉터 연계작업(create interceptor chain)을 생성할 것이다.

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

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

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

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

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

당신이 프록시 되기를 원하는 대상을 명시

CGLIB를 사용할지에 대한 명시

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

proxyTargetClass: 만약 우리가 인터페이스보다는 대상 클래스를 프록시화 한다면 true이다. 만약 이것이 true이면 우리는 CGLIB를 사용할 필요가 있다.

optimize: 생성된 프록시를 위해 공격적인 최적화를 적용할지에 대한 값. 만약 당신이 관련 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프록시는 사용될것이다.

interceptorNames: 적용하기 위한 Advisor, 인터셉터 또는 다른 advice이름의 문자열 배열. 정렬은 중요하다. 처음들어와서 처음 제공하는 그것이다. 리스트내 첫번째 인터셉터는 만약 이것이 통상의 MethodInterceptor 나 BeforeAdvice에 관계한다면 첫번째 호츨의 인터셉터를 가능하게 할것이다.

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

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

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

5.5.3. 프록시 인터페이스
액션내 ProxyFactoryBean의 간단한 예제를 보자. 이 예제는 다음을 포함한다.

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

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

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의 절렬은 중요하다.

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

Person person = (Person) factory.getBean("person");
같은 IoC컨텍스트내 다른 빈즈는 보통의 자바객체를 사용하는 것처럼 강력한 의존성을 표시할수 있다.

[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]

[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대상를 얻는것이 가능할때 실질적으로 장점이 될수 있다.

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

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

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

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

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

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

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

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

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

[bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"]
[property name="target"][ref bean="service"/][/property]
[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"/]


5.6. 편리한 프록시 생성
종종 우리는 트랜잭션 관리와 같은 하나의 aspect에만 관심을 가지지 때문에 ProxyFactoryBean의 모든 기능이 필요하지 않다.

특정 aspect에 집중하기를 원할때 우리가 AOP프록시를 생성하기 위해 사용할수 있는 편리한 많은 factory가 있다. 다른 장에서 언급되었기 때문에 우리는 여기서 그것들 중 몇가지의 빠른 조사결과를 제공할것이다.

5.6.1. TransactionProxyFactoryBean
Spring에 포함된 jPetStore 샘플 애플리케이션은 TransactionProxyFactoryBean의 사용을 보여준다.

TransactionProxyFactoryBean은 ProxyConfig의 하위클래스이다. 그래서 기초적인 설정은 ProxyFactoryBean와 공유한다. (위의 ProxyConfig 프라퍼티의 목록을 보라.)

jPetStore으로부터 다음의 예제는 이것이 어떻게 작동을 하는지 보여준다. ProxyFactoryBean를 사용하는 상태에서 여기에 대상 bean정의가 있다. 의존성은 대상 POJO("petStoreTarget")보다 프록시화된 factory bean정의(여기엔 "petStore")에 표시되어야 한다.

TransactionProxyFactoryBean은 대상과 트랜잭션 적인것과 요구되는 전달(propagation), 그리고 다른 셋팅이 될 방법을 명시하는 "트랜잭션 속성"에 대한 정보를 요구한다.

[bean id="petStoreTarget" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl"]
[property name="accountDao"][ref bean="accountDao"/][/property]
[!-- Other dependencies omitted --]
[/bean]

[bean id="petStore"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"]
[property name="transactionManager"][ref bean="transactionManager"/][/property]
[property name="target"][ref local="petStoreTarget"/][/property]
[property name="transactionAttributes"]
[props]
[prop key="insert*"]PROPAGATION_REQUIRED[/prop]
[prop key="update*"]PROPAGATION_REQUIRED[/prop]
[prop key="*"]PROPAGATION_REQUIRED,readOnly[/prop]
[/props]
[/property]
[/bean]
ProxyFactoryBean를 사용하는 상태에서 우리는 가장 상위레벨의 대상 bean에 대한 참조대신에 target프라퍼티의 값을 셋팅하는 내부 bean을 사용하는것을 선택해야만 한다.

TransactionProxyFactoryBean은 트랜잭션 속성에 기초를 둔 pointcut을 포함하는 트랜잭션 advisor를 자동적으로 생성한다. 그래서 오직 트랜잭션적인 메소드만이 advised된다.

TransactionProxyFactoryBean은 선인터셉터(preInterceptors)와 후인터셉터(postInterceptors) 프라퍼티를 사용하는 "선" 과 "후" advice의 명시를 허용한다. 트랜잭션 인터셉터의 앞이나 뒤 인터셉션 연계작업에 두기 위한 인터셉터의 객체 배열, 다른 advice또는 advisor를 가진다. 다음처럼 XML bean정의내 [list] 요소를 사용하여 활성화될수 있다.

[property name="preInterceptors"]
[list]
[ref local="authorizationInterceptor"/]
[ref local="notificationBeforeAdvice"/]
[/list]
[/property]
[property name="postInterceptors"]
[list]
[ref local="myAdvisor"/]
[/list]
[/property]
이 프라퍼티들은 위의 "petStore" bean정의에 추가될수 있다. 공통적인 사용법은 선언적인 보안과 함께 트랜잭션을 조합하는 것이다. EJB에 의해 제공되는 것과 유사한 접근법이다.

ProxyFactoryBean처럼 bean이름보다 실질적인 인스턴스 참조의 사용때문에 선,후 인터셉터는 오직 공유-인스턴스 advice만이 사용될수 있다. 게다가 그것들은 상태유지(stateful) advice를 위해서는 유용하지 않다. 이것은 TransactionProxyFactoryBean의 목적과 일치한다. 이것은 공통적인 트랜잭션 셋업의 간단한 방법을 제공한다. 만약 당신이 좀더 복잡하고, 사용자 정의된 AOP를 원한다면 일반적인 ProxyFactoryBean을 사용하거나 자동 프록시 생성자를 사용하는것을 고려하라.

특별히 우리가 많은 경우에 EJB의 대체물로 Spring AOP를 본다면 우리는 대부분의 advice가 꽤 일반적이고 공유-인스턴스 모델을 사용하는것을 알게된다. 선언적인 트랜잭션 관리와 보안 체크는 표준적인 예제이다.
TransactionProxyFactoryBean은 이것의 transactionManager 자바빈 프라퍼티를 통해 PlatformTransactionManager구현물에 의존한다. 이것은 JTA, JDBC또는 다른 전략에 기초를 둔 플러그인 가능한 트랜잭션 구현물을 허용한다. 이것은 AOP보다 Spring트랜잭션 추상화와 관련이 있다. 우리는 다음 장에서 트랜잭션 내부구조에 대해 언급할것이다.

만약 당신이 선언적인 트랜잭션 관리에만 관심을 가진다면 TransactionProxyFactoryBean은 ProxyFactoryBean보다 간단하고 좋은 해결방안이다.
5.6.2. EJB 프록시
다른 전용 프록시는 호출 코드에 의해 직접적으로 사용되기 위한 EJB "business methods" 인터페이스를 가능하도록 하는 EJB를 위한 프록시를 생성한다. 호출 코드는 JNDI룩업을 수행하거나 EJB생성 메소드를 사용하기 위해 필요하지 않다. 이것은 읽기가능한 성질과 구조적인 유연성에 명백한 향상을 가진다.

더 많은 정보를 위해서 이 문서의 Spring EJB서비스 관련 장을 보라.

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

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

[bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"]
[property name="transactionManager"][ref local="transactionManager"/][/ref][/property]
[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로 셋팅해야만 하고 반면에 애플리케이션 컨텍스트는 실질적으로 이것을 먼저 인스턴스화할 것이다.

5.8. 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모두의 수퍼클래스이다.

IoC프레임워크를 사용해서 AOP프록시 생성을 통합하는 것은 대부분의 애플리케이션에서 최상의 선택이다. 우리는 당신이 일반적인 것처럼 AOP를 사용해서 자바코드로 부터 설정을 구체화하는것을 추천한다.
5.9. 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);
이것은 비록 정확한 사용법이 의심스럽지 않더라도 제품내 비지니스 객체의 advice를 변경하는 것이 현명한것인지(advisable) 아닌지 의심스러울 것이다. 어쨌든 이것은 개발에서 매우 유용할수 있다. 예를 들면, 테스트에서 내가 테스트하길 원하는 메소드 호출내에서 얻어지는 인터셉터나 다른 advice의 형태내 테스트코드를 추가하는것이 가능하게 되는것이 매우 유용하다는 것을 때때로 발견하곤 한다.(예를 들어, advice는 그 메소드(예를 들면, 롤백을 위해 트랜잭션을 표시하기 전에 데이터베이스가 정확하게 수정되었는지 체크하기 위해 SQL수행하는것) 생성된 트랜잭션 내부에서 얻어질수 있다.
당신이 프록시를 생성하는 방법에 대해 의존하라. 당신은 Advised isFrozen()메소드가 true를 반환하고 추가나 삭제를 통해 advice를 변경하는 어떤 시도가 AopConfigException을 결과로 보내는 경우에 frozen 플래그를 셋팅할수 있다. advised 객체의 상태를 고정하기 위한 능력은 몇몇 경우에 유용하다. 예를 들면 보안 인터셉터를 제거하는 호출 코드를 제한하기 위해. 이것은 아마도 수행 advice 변경이 요구되지 않는다면 공격적인 최적화를 허용하기 위해 Spring 1.1내에서 사용된다.

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

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

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

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

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

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

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

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

[bean id="jdkBeanNameProxyCreator"
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에 다르게 적용될것이다.

5.10.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 id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"]
[/bean]

[bean id="txAdvisor"
autowire="constructor"
class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"]
[property name="order"][value]1[/value][/property]
[/bean]

[bean id="customAdvisor"
class="com.mycompany.MyAdvisor"]
[/bean]

[bean id="businessObject1"
class="com.mycompany.BusinessObject1"]
[!-- Properties omitted --]
[/bean]

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

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

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

5.10.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 id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"]
[/bean]

[bean id="transactionAttributeSource"
class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"
autowire="constructor"]
[/bean]

[bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor"
autowire="byType"]
[/bean]

[bean id="transactionAdvisor"
class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"
autowire="constructor" ]
[/bean]

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

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

[bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/]
만약 당신이 선언적인 트랜잭션 관리만을 요구한다면 이러한 일반적인 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"
singleton="false"
/]

[bean id="lockableAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor"
singleton="false"
]
[property name="pointcut"]
[ref local="myAttributeAwarePointcut"/]
[/property]
[property name="advice"]
[ref local="lockMixin"/]
[/property]
[/bean]

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

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

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

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

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

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

[bean id="swapper"
class="org.springframework.aop.target.HotSwappableTargetSource"]
[constructor-arg][ref local="initialTarget"/][/constructor-arg]
[/bean]

[bean id="swappable"
class="org.springframework.aop.framework.ProxyFactoryBean"
]
[property name="targetSource"]
[ref local="swapper"/]
[/property]
[/bean]
위의 swap() 호출은 스왑가능한 bean의 대상을 변경한다. 그 bean에 대한 참조를 유지하는 클라이언트는 변경을 인식하지 못할것이지만 새로운 대상에 즉시 도달할것이다.

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

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

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

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

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

[bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
singleton="false"]
... properties omitted
[/bean]

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

[bean id="businessObject"
class="org.springframework.aop.framework.ProxyFactoryBean"
]
[property name="targetSource"][ref local="poolTargetSource"/][/property]
[property name="interceptorNames"][value]myInterceptor[/value][/property]
[/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 local="poolTargetSource" /][/property]
[property name="targetMethod"][value]getPoolingConfigMixin[/value][/property]
[/bean]
이 advisor는 AbstractPoolingTargetSource클래스의 편리한 메소드를 호출하고 나아가 MethodInvokingFactoryBean의 사용하여 얻을수 있다. 이 advisor의 이름(여기 "poolConfigAdvisor")은 풀링된 객체를 드러내는 ProxyFactoryBean내 인터셉터 이름의 목록이 될수 있다.

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

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
풀링 비상태유지(stateless) 서비스 객체는 언제나 필요한것은 아니다. 우리는 이것이 대부분의 비상태유지(stateless) 객체가 근본적으로 쓰레드에 안전하고 인스턴스 풀링은 자원이 캐시된다면 골치거리가 되는것처럼 디폴트 선택이 될것이라고 믿지는 않는다.
좀더 간단한 풀링은 자동프록싱을 사용하여 사용가능하다. 어떤 autoproxy생성자에 의해 사용될수 있는 TargetSources 셋팅은 가능하다.

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

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

[bean id="prototypeTargetSource"
class="org.springframework.aop.target.PrototypeTargetSource"]
[property name="targetBeanName"][value]businessObjectTarget[/value][/property]
[/bean]
여기엔 오직 하나의 프라퍼티(대상 빈의 이름)가 있다. 상속은 일관적인 명명을 확실히 하기 위한 TargetSource구현물내 사용되었다. 풀링 대상 소스를 사용하는 것처럼 대상 bean은 프로토타입 bean정의가 되어야만 한다.

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


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


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

5.12. 새로운 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를 참조하라.

5.13. 추가적으로 읽을거리와 자원들
나는 AOP에 대한 소개를 위해 Ramnivas Laddad (Manning, 2003)에 의해 쓰여진 훌륭한 AspectJ in Action을 추천한다.

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

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

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

만약 당신이 Spring AOP의 좀더 향상된 기능에 관심이 있다면 테스트 슈트를 살펴보라. 테스트 적용범위는 90%이상이다. 그리고 이것은 이 문서에서 언급되지 않은 향상된 기능을 설명한다..

5.14. 로드맵
Spring의 나머지와 같은 Spring AOP는 활발히 개발되고 있다. 핵심 API는 안정적이다. Spring의 나머지처럼 AOP프레임워크는 기초적인 디자인을 보존하는 동안 확장을 가능하게 하는 매우 모듈적이다. 다양한 향상은 이전버전과의 호환성을 보존하는 Spring 1.1에 계획되어 있다. 그것들은 다음을 포함한다.

성능향상: AOP프록시의 생성은 Strategy인터페이스를 통해 factory에 의해 다루어진다. 게다가 우리는 사용자 코드나 핵심 구현에 영향없이 추가적인 AopProxy타입을 지원할수 있다. CGLIB를 위한 명백한 성능 최적화는 1.0.3버전에 계획되어 있다. 이 경우 Spring 1.1의 최적화는 advice가 수행시 변경되지 않을것이다. 이것은 AOP프레임워크의 오버헤드를 명백하게 감소시킨다. 어쨌든 AOP프레임워크의 오버헤드는 일반적인 사용법에서는 문제가 되지 않는다.

좀더 의미있는 pointcut: 현재 Spring은 의미있는 Pointcut인터페이스를 제공한다. 하지만 우리는 좀더 pointcut구현물을 추가함으로써 값을 추가할수 있다. 우리는 Spring 설정파일내에서 사용되기 위한 AspectJ pointcut표현을 허용할 AspectJ와의 통합을 본다. 그리고 만약 당신이 유용한 pointcut에 기여하길 바란다면 그렇게 해달라.

대부분의 명백한 향상은 AspectJ커뮤니티와 상호작용할 AspectJ와의 관련 통합일것이다. 우리는 이것이 다음과 같은 영역에서 Spring과 AspectJ둘다를 위한 명백한 이득을 제공할 것이라는것을 믿는다.

Spring IoC를 사용하여 설정되기 위한 AspectJ aspect허용하기. 이것은 선호하는 애플리케이션으로 AspectJ aspect를 Spring aspect가 애플리케이션 IoC컨텍스트와의 통합과 같은 방법으로 통합하는것의 가능성을 가진다.

AspectJ pointcut표현을 대상 Spring advice를 위한 Spring설정으로 허용하기. 이것은 우리 자신의 pointcut표현언어를 디자인하는데 명백한 이득을 가진다. AspectJ는 둘다 잘되고 문서화가 잘되어 있다.

이러한 통합 둘다 Spring 1.1에서 반드시 사용가능하게 될것이다.

댓글

이 블로그의 인기 게시물

[LINUX] CentOS 부팅시 오류 : UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY

[MSSQL] 데이터베이스가 사용 중이어서 배타적으로 액서스할 수 없습니다

구글코랩) 안전Dream 실종아동 등 검색 오픈API 소스를 공유합니다. (구글드라이브연동, 이미지 수집 소스)