https://sundaland.tistory.com/89
[ ▶ Declaring Advice ]
어드바이스는 포인트컷 표현식과 연관되어 있으며, 포인트컷에 의해 매칭된 메서드 실행 전, 후 또는 실행 중에 실행된다. 포인트컷 표현식인 인라인 포인트 컷이거나 named pointcut을 참조할 수 있다.
[ ▷ Before Advice ]
@Before 어노테이션을 사용하여 어드바이스를 선언할 수 있다.
▼ 인라인 포인트컷 표현식을 사용한 예제
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
▼ 이름이 지정된 인라인 포인트컷 표현식을 사용한 예제
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("com.xyz.CommonPointcuts.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
[ ▷ After Returuning Advice ]
After returning 어드바이스는 매칭된 메서드 실행이 정상적으로 리턴될 때 실행된다.
@AfterReturning 어노테이션을 사용하여 이를 선언할 수 있다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("execution(* com.xyz.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
동일한 어드바이스 내에 여러 개의 어드바이스 선언 및 다른 멤버들을 가질 수 있다. 여기서는 각각의 효과에 집중하기 위해 하나의 어드바이스 선언만을 보여준다.
때로는 어드바이스 본문에서 리턴된 실제 값에 접근해야 할 때가 있다. @AfterReturning의 리턴 값을 바인딩하는 형태를 사용하여 이에 접근할 수 있다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="execution(* com.xyz.dao.*.*(..))",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}
returning 속성에서 사용된 이름 (returning="retVal")은 어드바이스 메서드의 파라미터 이름과 일치해야 한다.
예를 들어, 위 어드바이스 메서드 doAccessCheck의 첫 번째 파라미터 이름은 retVal이어야 한다.
메스드 실행이 리턴될 때, 이 메서드가 리턴 값은 해당 어드바이스 파라미터의 아규먼트 값으로 어드바이스 메서드에 전달된다. 또한 returning 절은 지정된 타입 (Object)의 값을 리턴하는 메서드 실행만 매칭되도록 제한한다.
After returning 어드바이스를 사용할 때 완전히 다른 참조는 리턴할 수 없다.
[ ▷ After Throwing Advice ]
Throwing 어드바이스는 매치된 메서드 실행이 예외를 던지며 종료될 때 실행된다.
@AfterThrowing 어노테이션을 사용하여 선언할 수 있다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing("execution(* com.xyz.dao.*.*(..))")
public void doRecoveryActions() {
// ...
}
}
종종 특정 타입의 예외가 발생할 때만 어드바이스가 실행되도록 하고, 예외 본문에 접근할 필요가 있다. throwing 속성을 사용하면 매칭을 제한할 수 있으며 (필요한 경우, 그렇지 않으면 Throwalbe을 사용), 던저진 예외를 어드바이스 메서드의 파라미터로 바인딩할 수 있다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="execution(* com.xyz.dao.*.*(..))",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
}
throwing 속성에 사용된 이름은 어드바이스 메서드의 파라미터 이름과 일치해야 한다. 메서드 실행이 예외를 던지며 종료될 때, 해당 예외가 어드바이스 메서드의 해당 아규먼트로 전달된다. throwing 절은 지정된 타입의 예외를 던지는 메서드 실행으로만 매칭을 제한한다. (이 경우 DataAccessExcption)
@AfterThrowing 어드바이스 일반적인 예외 처리 콜백을 나타내지 않는다는 점에 유의해야 한다. 구체적으로, @AfterThrowing 어드바이스 메서드는 조인 포인트 (사용자 선언 타겟 메서드) 자체에서 발생한 예외만 수신해야 하며, 동반된 @After 또는 @AfterReturning 메서드에서 발생한 예외는 수신하지 않는다.
[ ▷ After (Finally) Advice ]
After (finally) 어드바이스는 매치된 메서드 실행이 종료될 때 실행된다. @After 어노테이션을 사용하여 선언된다. After 어드바이스는 정상적인 리턴과 예외 조건 모두를 처리할 준비가 되어 있어야 한다. 일반적으로 리소스를 해제하거나 유사한 목적으로 사용된다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("execution(* com.xyz.dao.*.*(..))")
public void doReleaseLock() {
// ...
}
}
AspectJ에서 @After 어드바이스는 try-catch 문의 finally 블록과 유사한 after finally 어드바이스로 정의된다. 이는 조인 포인트 (사용자가 선언한 타겟 메서드)에서 정상적으로 리턴되든 예외가 발생하든 모든 결과에 대해 호출된다. 이는 성공적인 장상 리턴에만 적용되는 @AfterReturning과는 대조적이다.
[ ▷ Around Advice ]
Around 어드바이스는 매치된 메서드 실행의 주변 (around)에서 실행된다. 이 어드바이스는 메서드가 실행되기 전후에 작업을 수행하고, 메서드가 실제로 실행될지, 언제 실행될지, 그리고 어떻게 실행될지를 결정할 수 있는 기회를 제공한다.
Around 어드바이스는 주로 메서드 실행 전후에 상태를 thread-safe 방식으로 공유해야 할 때 사용된다.
예를 들어, 타이머를 시작하고 정지할 때 사용할 수 있다.
항상 요구 사항을 충족하는 가장 단순한 형태의 어드바이스를 사용해야 한다. 예를 들어, before 어드바이스로 충분하다면 around 어드바이스를 사용하지 않아야 한다.
Around 어드바이스는 메서드 실행 전 후에 추가 작업을 수행할 수 있게 해주는 AOP 기능이다.
- @Around 어노테이션: Around 어드바이스로 사용할 메서드에 @Around 어노테이션을 붙인다. 이 어노테이션을 통해 해당 메서드가 어드바이스 메서드로 동작하게 된다.
- 리턴 타입: Around 어드바이스 메서드는 항상 Object 타입을 리턴해야 한다. 이는 타겟 메서드의 리턴 값을 처리하기 위함이다.
- 첫 번째 파라미터: 어드바이스 메서드는 ProceedingJoinPoint 타입의 첫 번째 파라미터를 가져야 한다. 이 파라미터는 실제로 호출될 타겟 메서드나 그 메서드의 정보를 나타낸다.
- proceed() 메서드 호출: Around 어드바이스 메서드 안에서 proceed() 메서드를 호출해야 타겟 메서드가 실행된다. 이 메서드를 호출하지 않으면 타겟 메서드가 실행되지 않는다.
- 아규먼트 전달: proceed()를 호출할 때 아무런 아규먼트도 전달하지 않으면, 원래 (타겟)메서드에 전달될 아규먼트들이 그대로 사용된다. 하지만 만약 전달하는 아규먼트를 변경하고 싶다면, Object[] 베열을 아규먼트로 전달하는 proceed(Object[] args) 메서드를 사용할 수 있다. 이 배열 안에 있는 값 들이 타겟 메서드의 새로운 아규먼트로 사용된다.
이렇게 Around 어드바이스를 사용하면 메서드 실행 전 후에 로직을 추가하거나, 메서드의 아규먼트를 변경하여 실행할 수 있다.
[ ▷ 스프링 AOP와 AspectJ의 procced() 메서드 동작 차이와 호환성 고려사항 ]
1. Proceed 메서드의 기본 개념
- proceed() 메서드는 around 어드바이스에서 사용되는 메서드로, 타겟 메서드를 실행시킨다.
- 이 메서드를 호출하지 않으면 타겟 메서드가 실행되지 않는다.
2. 두 가지 AOP 접근 방식: 스프링 AOP vs. AspectJ
- 스프링 AOP: 스프링 프레임워크에서 제공하는 AOP는 주로 프록시 기반으로 동작한다. 실행 지점 (Execution Join Points)에만 적용되며, 상대적으로 간단하다.
- AspectJ: AspectJ는 컴파일 타임 또는 로드 타임에 바이트코드를 조작하는 강력한 AOP 프레임워크이다. 더 복잡한 AOP 기능을 제공한다.
3. proceed() 메서드의 동작 차이점
3.1 스프링 AOP 에서의 proceed()
- 스프링 AOP에서는 proceed(Object[] args)를 호출할 때, 이 args 배열은 타겟 메서드에 전달될 인자들을 나타낸다.
- proceed()를 호출할 때 인자를 제공하지 않으면 원래의 인자들이 사용된다. 만약 새로운 인자 배열을 제공하면, 타겟 메서드는 이 새로운 인자들을 사용하여 실행된다.
3.2 AspectJ에서의 proceed()
- AspectJ에서 작성된 어라운드 어드바이스에서는 proceed() 메서드를 호출할 떄 전달하는 인자들이 조금 다르게 처리된다.
- proceed()에 전달되는 인자의 개수는 어드바이스가 받는 인자 수와 일치해야 한다.
- 어드바이스 메서드가 두 개의 인자를 받는다면, proceed() 메서드도 두 개의 인자를 받아야 한다. 이 인자들은 원래 타겟 메서드레 전달될 인자와는 다를 수 있다.
- 인자가 전달된 위치에 따라 타겟 메서드에 적용되는 값이 달라질 수 있다. 즉, 인자의 위치에 따라 원래 인자가 대체될 수 있다.
4. 호환성 문제
- 스프링 AOP와 AspectJ는 proceed() 메서드를 처리하는 방식에 차이가 있기 때문에, 동일한 어드바이스 코드를 스프링 AOP와 AspectJ에서 모두 사용하려면 이 차이를 고려해야 한다.
- @AspectJ 어노테이션을 사용하여 AspectJ 컴파일러와 위버를 사용하는 경우, 스프링 AOP의 단순한 인자 처리 방식과 차이를 이해해야 한다.
5. 해결 방안
스프링 AOP와 AspectJ에서 모두 호환되는 어드바이스 코드를 작성하는 방법이 있다.
스프링 AOP와 AspectJ는 proceed() 메서드의 아규먼트 처리 방식에 차이가 있다. 스프링 AOP는 아규먼트 배열을 통해 타겟 메서드에 전달할 아규먼트들을 쉽게 변경할 수 있지만, AspectJ에서는 어드바이스 메서드의 아규먼트 개수와 proceed() 메서드에 전달되는 아규먼트 개수가 일치해야 한다. 이 차이를 염두에 두고 코드를 작성해야 한다.
Around 어드바이스에서 리턴된 값은 메서드 호출자가 보게 되는 리턴 값이다. 예를 들어, 간단한 캐싱 어드바이스는 캐시에 값이 있으면 그 값을 리턴하고, 그렇지 않으면 proceed()를 호출하고 그 값을 리턴할 수 있다. proceed는 어드바이스 본문 내에서 한 번, 여러 번 또는 전혀 호출되지 않을 수 있다. 이들은 모두 합법적이다.
Around 어드바이스 메서드의 리턴 타입을 void로 선언하면, null이 호출자에게 항상 리턴되며, proceed 호출의 결과가 무시된다. 따라서 Around 어드바이스 메서드는 Object를 리턴 타입으로 선언하는 것이 좋다. 어드바이스 메서드는 일반적으로 proceed() 호출에서 리턴된 값일 리턴해야 한다. 기본 메서드가 void 리턴 타입이더라도 마찬가지다. 그러나 캐시된 값, 래핑된 값 또는 다른 값을 리턴할 수도 있다. 이는 사용 사례에 따라 다르다.
▼ around 어드바이스를 사용하는 방법
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("execution(* com.xyz..service.*.*(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// 스톱워치 시작
Object retVal = pjp.proceed();
// 스톱워치 정지
return retVal;
}
}
[ ▷ Advice Parameters ]
어떤 어드바이스 메서드든 첫 번째 파라미터로 org.aspectj.lang.JoinPoint 타입의 파라미터를 선언할 수 있다. 특히 around 어드바이스는 ProceedingJoinPoint 타입의 첫 번째 파라미터를 선언해야 하는데, JoinPoint의 하위 클래스이다.
- getArgs(): 메서드 아규먼트를 처리한다.
- getThis(): 프록시 객체를 리턴한다.
- getTarget(): 타겟 객체를 리턴한다.
- getSignature(): 현재 어드바이스가 적용되고 있는 메서드의 설명을 반환한다.
- toString(): 어드바이스가 적용되는 메서드에 대한 유용한 설명을 출력한다.
[ ▷ Passing Parameters to Advice ]
아규먼트 값을 어드바이스 본문에 전달하기 위해 args의 바인딩 형식을 사용할 수 있다. args 표현식에서 타입 이름 대신 파라미터 이름을 사용하면 해당 파라미터에 전달된 값이 어드바이스가 호출될 때 전달된다. 예를 들어 DAO 작업을 수행하는 메서드 중 첫 번째 파라미터로 Account 객체를 받는 메서드의 실행을 어드바이스로 처리하고 싶다면 아래와 같이 작성할 수 있다.
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) {
// ...
}
여기서 args(account, ..) 부분은 두 가지 역할을 한다. 첫째. 아규먼트가 최소 하나 이상 존재하고, 그 아규먼트가 Account의 인스턴스인 경우에만 메서드 실행을 매칭한다. 둘째, 실제 Account 객체를 accout 파라미터를 통해 advice에서 사용할 수 있도록 한다.
다른 방식으로, Account 객체 값을 제공하는 포인트컷을 선언하고, 그 포인트컷을 advice에서 참조하는 방법이 있다.
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
// ...
}
프록시 객체 (this), 타겟 객체 (target), 어노테이션 (@within, @target, @annotation, @args)도 유사하게 바인딩할 수 있다.
▼ @Auditable 어노테이션이 적용된 메서드 실행을 매칭하고, 감사 코드를 추출하는 예제
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
AuditCode value();
}
▼ @Auditable 메서드의 실행을 매칭하는 어드바이스
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
AuditCode code = auditable.value();
// ...
}
[ ▷ Advice Parameters and Generics ]
스프링 AOP는 클래스 선언 및 메서드 파라미터에서 사용된 제너릭을 처리할 수 있다.
public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param);
}
특정 파라미터 타입에 대해 메서드 타입의 인터셉션을 제한하려면, advice 파라미터를 인터셉트하려는 메서드이 파라미터 타입에 연결할 수 있다.
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
// Advice 구현
}
com.intheeast.aspectj.declaringadvice.service.Sample+ 는 Sample 인터페이스뿐만이 아니라 Sample 인터페이스를 구현하는 모든 클래스에도 적용된다는 의미이다. (SampleService 클래스의 메서드에도 적용된다.)
타입 패턴에서는 + 는 해당 타입과 그 서브타입을 모두 포함시킨다는 의미이다.
그러나 제너릭 컬렉션에 대해서는 이 접근 방식이 작동하지 않는다. 아래 예제와 같은 포인트컷은 정의할 수 없다.
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
// Advice 구현
}
이와 유사한 작업을 하려면, 파라미터 타입을 Collection<?>으로 지정하고 엘리먼트의 타입을 수동으로 확인해야 한다.
[ ▷ Determining Argument Names ]
어드바이스 호출 시 파라미터 바인딩은 포인트컷 표현식에서 사용된 이름과 어드바이스 및 포인트컷 메서드 서명에 선언된 파라미터 이름을 매칭하는 방식으로 이루어진다.
이 섹션에서는 AspectJ API가 파라미터 이름을 아규먼트 이름이라고 언급하기 때문에 아규먼트와 파라미터라는 용어를 혼용하여 사용한다.
스프링 AOP는 파라미터 이름을 결정하기 위해 다음과 같은 ParameterNameDiscoverer 구현체를 사용한다. 각 구현체는 파라미터 이름을 발견할 기회를 제공받으며, 가장 먼저 성공한 구현체가 사용된다. 등록된 탐색기 중 파라미터 이름을 결정할 수 있는 구현체가 없으면 예외가 발생한다.
- AspectJAnnotationParameterNameDiscoverer: 해당 어드바이스 또는 포인트컷 어노테이션의 argNames 속성을 통해 사용자가 명시적으로 지정한 파라미터 이름을 사용한다.
- KotlinReflectionParameterNameDiscoverer: Kotlin 리플렉션 API를 사용하여 파라미터 이름을 결정한다. 이러한 API가 클래스패스에 있는 경우에만 사용된다.
- StandardReflectionParameterNameDiscoverer: 표준 java.lang.reflect.Parameter API를 사용하여 파라미터 이름을 결정한다. javac에 --parameters 플래그를 사용해 컴파일된 코드가 필요하다.
- AspectJAdviceParameterNameDiscoverer: 포인트컷 표현식, 반환, throw 절에서 파라미터 이름을 유추한다,
[ ▷ Explicit Argument Names ]
@AspectJ 어드바이스 및 포인트컷 어노테이션에는 argNames 속성이 있다. 이를 사용하여 어노테이션 메서드의 아규먼트 이름을 지정할 수 있다.
@AspectJ apsect가 AspectJ 컴파일라(ajc)로 컴파일된 경우, 디버그 정보 없이도 필요한 정보가 유지되므로 argNames 속성을 추가할 필요가 없다.
마찬가지로, @AspectJ aspect가 javac를 사용하여 --parameters 플래그로 컴파일된 경우에도 argNames 속성을 추가할 필요가 없다.
▼ argNames 속성을 사용하는 방법
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)",
argNames = "bean,auditable")
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code and bean
}
첫 번째 파라미터가 JoinPoint, ProceedingJoinPoint 또는 JoinPoint.StaticPart 타입인 경우, argNames 속성에서 해당 파라미터의 이름을 생략할 수 있다. 예를 들어 앞서 언급한 어드바이스를 변경하여 join point 객체를 수신하도록 한다면, argNames 속성에서 이를 포함할 필요가 없다.
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)",
argNames = "bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}
JoinPoint, ProceedingJoinPoint 또는 JoinPoint.StaticPart 타입의 첫 번째 파라미터에 대한 특별 처리는 조인 포인트 컨텍스트를 수집하지 않는 어드바이스 메서드에 특히 편리하다가.
이러한 상황에서는 argNames 속성을 생략할 수 있다. 예를 들어, 다음 어드바이스는 argNames 속성을 선언할 필요가 없다.
@Before("com.xyz.Pointcuts.publicMethod()")
public void audit(JoinPoint jp) {
// ... use jp
}
[ ▷ Proceeding with Arguments ]
스프링 AOP와 AspectJ에서 일관되게 작동하는 proceed 호출을 작성하는 방법에 대해 설명한다. 해결책은 메서드 파라미터 각각을 순서대로 어드바이스 시그니처에 바인딩하는 것이다.
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
"args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
String accountHolderNamePattern) throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
}
이 경우 대부분의 경우처럼 이러한 바인딩이 필요하다.
[ ▷ Advice Ordering ]
스프링 AOP는 어드바이스 실행 순서를 결정하기 위해 AspectJ와 동일한 우선순서 규칙을 따른다. 우선순위가 가장 높은 어드바이스는 진입 시에 먼저 실행된다. 따라서 두 개의 before 어드바이스가 있다면, 우선 순위가 가장 높은 어드바이스가 먼저 실행된다. 조인 포인트에서 종료 시에는 우선 순위가 가장 높은 어드바이스가 마지막에 실행된다. 따라서 두 개의 after 어드바이스가 있다면, 우선순위가 가장 높은 어드바이스가 두 번째로 실행된다. 서로 다른 액스펙트에 정의된 두 어드바이스가 동일한 조인 포인트에서 모두 실행되어야 할 경우, 별도로 지정하지 않으면 실행 순서는 정의되지 않는다. 그러나 우선 순위를 지정하여 실행 순서를 제어할 수 있다. 이는 일반적인 스프링 방식으로, 애스펙트에 @Order 어노테이션을 추가하여 가능하다. 두 액스펙트가 있을 때, Ordered.getOrder() 메서드에서 더 낮은 값을 반환하는 애스펙트 (또는 어노테이션 값이 더 낮은 애스펙트)가 더 높은 우선순위를 가지게 된다.
특정 애스펙트의 각기 다른 어드바이스 유형들은 개념적으로 조인 포인트에 직접 적용되도록 설계되었다. 따라서, @AfterThrowing 어드바이스 메서드는 함께 동작하는 @After나 @AfterReturning 메서드로부터 예외를 전달받지 않도록 해야 한다.
동일한 @Aspect 클래스 내에서 동일한 조인 포인트에서 실행되어야 하는 어드바이스 메서드들은 다음과 같은 순서로 어드바이스 유형에 따라 우선 순위가 지정된다. 가장 높은 우선 순위에서 낮은 우선 순위로 나열하면 @Around, @Before, @After, @AfterReturning, @AfterThrowing이다. 하지만 @After 어드바이스 메서드는 동일한 애스텍트 내에서 @ AfterReturning 이나 @AfterThrowing 어드바이스 메서드 이후에 효과적으로 호출되며, 이는 AspectJ의 after finally advice 개념을 따른 것이다.
동일하s @Aspect 클래스에서 정의된 동일한 유형의 두 개의 어드바이스 (예를 들어, 두 개의 @After 어드바이스 메서드)가 동일한 조인 포인트에서 실행되어야 하는 경우, 그 순서는 정의되지 않는다. (이는 javac로 컴파일된 클래스에서 리플렉션을 통해 소스 코드 선언 순서를 가져올 방법이 없기 때문이다.) 이러한 경우, 각 @Aspect 클래스네 내에서 조인 포인트마다 하나의 어드바이스 메서드로 통합하거나, 각 어드바이스를 별도의 @Aspect 클래스로 리팩터링하여 Ordered나 @Oredr를 통해 애스팩트 레벨에서 순서를 지정하는 것이 좋다.
[ ▷ Introductions ]
Introductions ( AspectJ에서는 inter-type declarations라고도 한다)는 특정 aspect가 어드바이스된 객체가 주어진 인터페이스를 구현하도록 선언하고, 그 객체들을 대신하여 해당 인터페이스의 구현을 제공할 수 있게 한다.
@DeclareParents 어노테이션을 사용하여 Introductions을 만들 수 있다. 이 어노테이션은 매칭되는 타입에 새로운 부모 (즉, 새로운 인터페이스 구현)를 가지도록 선언하는데 사용된다. 예를 들어, UsageTracked라는 인터페이스와 이 인터페이스를 구현한 DefaultUsageTracked라는 클래스가 있다고 가정할때, 다음의 aspect는 모든 서비스 인터페이스 구현체들이 UsageTracked 인터페이스를 구현하도록 선언한다,
@Aspect
public class UsageTracking {
@DeclareParents(value="com.xyz.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("execution(* com.xyz..service.*.*(..)) && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
구현할 인터페이스에는 어노테이션이 적용된 필드 타입에 의해 결정된다. @DeclareParents 어노테이션의 value 속성은 AspectJ 타입 패턴을 사용한다. 매칭되는 타입의 모든 빈은 UsageTracked 인터페이스를 구현하게 된다.
위 예제의 before 어드바이스에서 볼 수 있듯이, 서비스 빈은 UsageTracked 인터페이스의 구현체로 직접 사용할 수 있다. 빈을 프로그래밍 방식으로 접근할 때는 다음과 같이 작성할 수 있다.
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
'스프링 AOP' 카테고리의 다른 글
Java Instrument API vs ASM(Abstract Syntax Manipulation) (0) | 2024.09.03 |
---|---|
Java Instrumentation API (0) | 2024.09.03 |
@AspectJ support (0) | 2024.08.31 |
Delegate (0) | 2024.08.28 |
Spring AOP APIs 3[Concise Proxy Definitions, Creating AOP Proxies Programmatically with the ProxyFactory, Manipulating Advised Objects, Using the "auto-proxy" facility] (0) | 2024.08.28 |