Loading...
Spring Framework Reference Documentation 7.0.2의 Declaring Advice의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Advice는 pointcut expression과 연결되며, pointcut에 의해 매칭되는 메서드 실행의 이전, 이후, 또는 주변에서 실행됩니다. pointcut expression은 inline pointcut 이거나 named pointcut에 대한 reference일 수 있습니다.
@Before 어노테이션을 사용하여 aspect 안에 before advice를 선언할 수 있습니다.
다음 예시는 inline pointcut expression을 사용합니다.
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.Before; 3 4@Aspect 5public class BeforeExample { 6 7 @Before("execution(* com.xyz.dao.*.*(..))") 8 public void doAccessCheck() { 9 // ... 10 } 11}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.Before 3 4@Aspect 5class BeforeExample { 6 7 @Before("execution(* com.xyz.dao.*.*(..))") 8 fun doAccessCheck() { 9 // ... 10 } 11}
named pointcut을 사용하면, 앞의 예시를 다음과 같이 다시 작성할 수 있습니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.Before; 3 4@Aspect 5public class BeforeExample { 6 7 @Before("com.xyz.CommonPointcuts.dataAccessOperation()") 8 public void doAccessCheck() { 9 // ... 10 } 11}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.Before 3 4@Aspect 5class BeforeExample { 6 7 @Before("com.xyz.CommonPointcuts.dataAccessOperation()") 8 fun doAccessCheck() { 9 // ... 10 } 11}
After returning advice는 매칭된 메서드 실행이 정상적으로 return될 때 실행됩니다.
@AfterReturning 어노테이션을 사용하여 선언할 수 있습니다.
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.AfterReturning; 3 4@Aspect 5public class AfterReturningExample { 6 7 @AfterReturning("execution(* com.xyz.dao.*.*(..))") 8 public void doAccessCheck() { 9 // ... 10 } 11}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.AfterReturning 3 4@Aspect 5class AfterReturningExample { 6 7 @AfterReturning("execution(* com.xyz.dao.*.*(..))") 8 fun doAccessCheck() { 9 // ... 10 } 11}
하나의 aspect 안에 여러 advice 선언(및 다른 멤버들)을 둘 수 있습니다.<br>이 예시들에서는 각각의 효과에 집중하기 위해 하나의 advice 선언만을 보여줍니다.
때때로, advice body에서 실제로 return된 값에 접근해야 할 필요가 있습니다.
그러한 접근을 얻기 위해, return 값을 binding하는 형태의 @AfterReturning을 사용할 수 있으며, 다음 예시와 같습니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.AfterReturning; 3 4@Aspect 5public class AfterReturningExample { 6 7 @AfterReturning( 8 pointcut="execution(* com.xyz.dao.*.*(..))", 9 returning="retVal") 10 public void doAccessCheck(Object retVal) { 11 // ... 12 } 13}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.AfterReturning 3 4@Aspect 5class AfterReturningExample { 6 7 @AfterReturning( 8 pointcut = "execution(* com.xyz.dao.*.*(..))", 9 returning = "retVal") 10 fun doAccessCheck(retVal: Any?) { 11 // ... 12 } 13}
returning attribute에 사용된 이름은 advice 메서드의 파라미터 이름과 일치해야 합니다.
메서드 실행이 return될 때, return 값은 해당 인수 값으로 advice 메서드에 전달됩니다.
returning 절은 또한 지정된 타입의 값을 return하는 메서드 실행에만 매칭을 제한합니다
(이 경우, 어떤 return 값이든 매칭되는 Object).
after returning advice를 사용할 때는 완전히 다른 reference를 return하는 것은 불가능하다는 점에 유의하십시오.
After throwing advice는 매칭된 메서드 실행이 exception을 던지며 종료될 때 실행됩니다.
다음 예시와 같이 @AfterThrowing 어노테이션을 사용하여 선언할 수 있습니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.AfterThrowing; 3 4@Aspect 5public class AfterThrowingExample { 6 7 @AfterThrowing("execution(* com.xyz.dao.*.*(..))") 8 public void doRecoveryActions() { 9 // ... 10 } 11}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.AfterThrowing 3 4@Aspect 5class AfterThrowingExample { 6 7 @AfterThrowing("execution(* com.xyz.dao.*.*(..))") 8 fun doRecoveryActions() { 9 // ... 10 } 11}
종종, 특정 타입의 exception이 던져질 때만 advice가 실행되기를 원하며,
또한 advice body에서 던져진 exception에 대한 접근이 필요할 때가 많습니다.
throwing attribute를 사용하여 매칭을 제한(원한다면 — 그렇지 않으면 exception 타입으로 Throwable을 사용)하고, 던져진 exception을 advice 파라미터에 binding할 수 있습니다.
다음 예시는 이를 수행하는 방법을 보여줍니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.AfterThrowing; 3 4@Aspect 5public class AfterThrowingExample { 6 7 @AfterThrowing( 8 pointcut="execution(* com.xyz.dao.*.*(..))", 9 throwing="ex") 10 public void doRecoveryActions(DataAccessException ex) { 11 // ... 12 } 13}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.AfterThrowing 3 4@Aspect 5class AfterThrowingExample { 6 7 @AfterThrowing( 8 pointcut = "execution(* com.xyz.dao.*.*(..))", 9 throwing = "ex") 10 fun doRecoveryActions(ex: DataAccessException) { 11 // ... 12 } 13}
throwing attribute에 사용된 이름은 advice 메서드의 파라미터 이름과 일치해야 합니다.
메서드 실행이 exception을 던지며 종료될 때, exception은 해당 인수 값으로 advice 메서드에 전달됩니다.
throwing 절은 또한 지정된 타입의 exception을 던지는 메서드 실행에만 매칭을 제한합니다
(이 경우, DataAccessException).
@AfterThrowing은 일반적인 exception handling 콜백을 나타내지 않는다는 점에 유의하십시오.<br>구체적으로,@AfterThrowingadvice 메서드는 join point(사용자가 선언한 target 메서드) 자체에서<br>발생한 exception만을 받아야 하며, 동반되는@After/@AfterReturning메서드에서 발생한 exception은<br>받지 않습니다.
After (finally) advice는 매칭된 메서드 실행이 종료될 때 실행됩니다.
이는 @After 어노테이션을 사용하여 선언됩니다.
After advice는 정상 return과 exception return 조건 모두를 처리할 준비가 되어 있어야 합니다.
이는 일반적으로 리소스 해제와 유사한 목적에 사용됩니다.
다음 예시는 after finally advice를 사용하는 방법을 보여줍니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.After; 3 4@Aspect 5public class AfterFinallyExample { 6 7 @After("execution(* com.xyz.dao.*.*(..))") 8 public void doReleaseLock() { 9 // ... 10 } 11}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.After 3 4@Aspect 5class AfterFinallyExample { 6 7 @After("execution(* com.xyz.dao.*.*(..))") 8 fun doReleaseLock() { 9 // ... 10 } 11}
AspectJ에서
@Afteradvice는 try-catch 구문의 finally 블록에 해당하는 "after finally advice"로<br>정의된다는 점에 유의하십시오. 이는 어떤 결과에 대해서도 호출되며, join point(사용자가 선언한<br>target 메서드)에서 정상 return이든 exception이 던져지든 모두 호출됩니다. 이는 성공적인 정상<br>return에만 적용되는@AfterReturning과 대조됩니다.
마지막 종류의 advice는 around advice입니다. Around advice는 매칭된 메서드 실행을 "둘러싸고" 실행됩니다. 이는 메서드가 실행되기 전과 후 모두에서 작업을 수행할 수 있는 기회를 가지며, 메서드가 실제로 언제, 어떻게, 심지어 실행될 것인지 여부까지도 결정할 수 있습니다.
Around advice는 thread-safe한 방식으로 메서드 실행 전후에 상태를 공유해야 할 때 자주 사용됩니다. 예를 들어, 타이머를 시작하고 정지하는 경우입니다.
요구사항을 충족하는 가장 약한 형태의 advice만을 항상 사용하십시오.<br>예를 들어, before advice로 충분한 경우 around advice를 사용하지 마십시오.
Around advice는 메서드에 @Around 어노테이션을 붙여 선언합니다.
이 메서드는 return 타입으로 Object를 선언해야 하며, 메서드의 첫 번째 파라미터는 ProceedingJoinPoint 타입이어야 합니다.
advice 메서드 body 안에서, underlying 메서드가 실행되도록 하려면 반드시 ProceedingJoinPoint에서 proceed()를 호출해야 합니다.
인수 없이 proceed()를 호출하면, 호출자의 원래 인수들이 underlying 메서드가 호출될 때 전달됩니다.
고급 use case를 위해, 인수 배열(Object[])을 받는 proceed() 메서드의 오버로드된 variant가 있습니다.
배열 안의 값들은 underlying 메서드가 호출될 때 인수로 사용됩니다.
Object[]와 함께 호출될 때의proceed동작은 AspectJ compiler에 의해 compile된 around advice에서의<br>proceed동작과 약간 다릅니다. 전통적인 AspectJ language로 작성된 around advice의 경우,<br>proceed에 전달되는 인수 수는 around advice에 전달되는 인수 수(underlying join point가<br>받는 인수 수가 아님)와 일치해야 하며, 특정 인수 위치에서 proceed에 전달된 값은 join point에서<br>해당 값이 binding된 entity에 대해 원래 값을 대체합니다(지금 당장은 이해가 되지 않더라도 걱정하지<br>마십시오).<br>Spring이 취하는 접근 방식은 더 단순하며, proxy 기반의 execution-only semantics와 더 잘 맞습니다.<br>Spring AOP와 AspectJ 모두에 대해 작성된@AspectJaspect를 compile하고 AspectJ compiler와 weaver에서<br>인수와 함께proceed를 사용할 때에만 이 차이점을 인지하고 있으면 됩니다.<br>Spring AOP와 AspectJ 양쪽 모두에서 100% 호환되는 방식으로 이러한 aspect를 작성하는 방법이 있으며,<br>이는 다음 advice 파라미터 섹션에서 논의됩니다.
Around advice에서 return된 값은 메서드 호출자가 보게 되는 return 값입니다.
예를 들어, 간단한 캐싱 aspect는 캐시에 값이 있으면 그 값을 return하고, 없으면 proceed()를 호출하고(그리고 그 값을 return) 할 수 있습니다.
proceed는 around advice body 안에서 한 번, 여러 번, 또는 전혀 호출되지 않을 수도 있다는 점에 유의하십시오.
이 모든 경우가 허용됩니다.
around advice 메서드의 return 타입을
void로 선언하면, 호출자에게 항상null이 return되며,<br>사실상proceed()호출의 결과를 무시하게 됩니다. 따라서 around advice 메서드는 return 타입으로<br>Object를 선언하는 것이 권장됩니다. underlying 메서드가voidreturn 타입을 가지더라도,<br>advice 메서드는 일반적으로proceed()호출에서 return된 값을 return해야 합니다.<br>그러나 use case에 따라, advice는 선택적으로 캐시된 값, wrapping된 값, 또는 다른 값을 return할 수<br>있습니다.
다음 예시는 around advice를 사용하는 방법을 보여줍니다:
1import org.aspectj.lang.annotation.Aspect; 2import org.aspectj.lang.annotation.Around; 3import org.aspectj.lang.ProceedingJoinPoint; 4 5@Aspect 6public class AroundExample { 7 8 @Around("execution(* com.xyz..service.*.*(..))") 9 public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { 10 // start stopwatch 11 Object retVal = pjp.proceed(); 12 // stop stopwatch 13 return retVal; 14 } 15}
1import org.aspectj.lang.annotation.Aspect 2import org.aspectj.lang.annotation.Around 3import org.aspectj.lang.ProceedingJoinPoint 4 5@Aspect 6class AroundExample { 7 8 @Around("execution(* com.xyz..service.*.*(..))") 9 fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? { 10 // start stopwatch 11 val retVal = pjp.proceed() 12 // stop stopwatch 13 return retVal 14 } 15}
Spring은 완전히 타입이 지정된 advice를 제공하며, 이는 (앞에서 returning과 throwing 예시에서 본 것처럼) 항상 Object[] 배열을 사용하는 대신 advice 시그니처에서 필요한 파라미터들을 선언한다는 의미입니다.
이 섹션의 뒷부분에서 인수 및 다른 context 값들을 advice body에서 사용할 수 있도록 만드는 방법을 살펴봅니다. 먼저, advice가 현재 advising하고 있는 메서드에 대해 알아낼 수 있는 generic advice를 작성하는 방법을 살펴보겠습니다.
JoinPoint에 대한 Access어떤 advice 메서드든, 첫 번째 파라미터로 org.aspectj.lang.JoinPoint 타입의 파라미터를 선언할 수 있습니다.
around advice는 첫 번째 파라미터로 JoinPoint의 서브클래스인 ProceedingJoinPoint 타입을 선언해야 한다는 점에 유의하십시오.
JoinPoint 인터페이스는 여러 유용한 메서드를 제공합니다:
getArgs(): 메서드 인수를 return합니다.getThis(): 프록시 객체를 return합니다.getTarget(): 타깃 객체를 return합니다.getSignature(): advice가 적용되고 있는 메서드에 대한 설명을 return합니다.toString(): advice가 적용되고 있는 메서드에 대한 유용한 설명을 출력합니다.자세한 내용은 javadoc을 참조하십시오.
이미 after returning 및 after throwing advice를 사용하여 return 값 또는 exception 값을 binding하는 방법을 살펴보았습니다.
인수 값을 advice body에서 사용할 수 있도록 만들기 위해, args의 binding form을 사용할 수 있습니다.
args expression에서 타입 이름 대신 파라미터 이름을 사용하면, advice가 호출될 때 해당 인수의 값이 파라미터 값으로 전달됩니다.
예시를 통해 더 명확해질 것입니다.
첫 번째 파라미터로 Account 객체를 받는 DAO operation의 실행에 advice를 적용하고, advice body에서 해당 account에 접근해야 한다고 가정해 봅시다.
다음과 같이 작성할 수 있습니다:
1@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)") 2public void validateAccount(Account account) { 3 // ... 4}
1@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)") 2fun validateAccount(account: Account) { 3 // ... 4}
pointcut expression의 args(account,..) 부분은 두 가지 목적을 수행합니다.
첫째, 메서드가 최소한 하나의 파라미터를 가지며, 그 파라미터에 전달된 인수가 Account 인스턴스인 메서드 실행에만 매칭을 제한합니다.
둘째, 실제 Account 객체를 account 파라미터를 통해 advice에서 사용할 수 있도록 만듭니다.
이를 작성하는 또 다른 방법은, join point에 매칭될 때 Account 객체 값을 "제공"하는 pointcut을 선언하고, advice에서 해당 named pointcut을 참조하는 것입니다.
이는 다음과 같이 보일 것입니다:
1@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)") 2private void accountDataAccessOperation(Account account) {} 3 4@Before("accountDataAccessOperation(account)") 5public void validateAccount(Account account) { 6 // ... 7}
1@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)") 2private fun accountDataAccessOperation(account: Account) { 3} 4 5@Before("accountDataAccessOperation(account)") 6fun validateAccount(account: Account) { 7 // ... 8}
자세한 내용은 AspectJ programming guide를 참조하십시오.
프록시 객체(this), 타깃 객체(target), 그리고 어노테이션들(@within,
@target, @annotation, @args)도 모두 유사한 방식으로 binding할 수 있습니다.
다음 예시들은 @Auditable 어노테이션이 붙은 메서드 실행을 매칭하고 audit code를 추출하는 방법을 보여줍니다:
다음은 @Auditable 어노테이션의 정의를 보여줍니다:
1@Retention(RetentionPolicy.RUNTIME) 2@Target(ElementType.METHOD) 3public @interface Auditable { 4 AuditCode value(); 5}
1@Retention(AnnotationRetention.RUNTIME) 2@Target(AnnotationTarget.FUNCTION) 3annotation class Auditable(val value: AuditCode)
다음은 @Auditable 메서드 실행을 매칭하는 advice를 보여줍니다:
1@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // (1) 2public void audit(Auditable auditable) { 3 AuditCode code = auditable.value(); 4 // ... 5}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
1@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // (1) 2fun audit(auditable: Auditable) { 3 val code = auditable.value() 4 // ... 5}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
Spring AOP는 클래스 선언 및 메서드 파라미터에서 사용되는 generics를 처리할 수 있습니다. 다음과 같은 generic 타입이 있다고 가정해 봅시다:
1public interface Sample<T> { 2 void sampleGenericMethod(T param); 3 void sampleGenericCollectionMethod(Collection<T> param); 4}
1interface Sample<T> { 2 fun sampleGenericMethod(param: T) 3 fun sampleGenericCollectionMethod(param: Collection<T>) 4}
advice 파라미터를 메서드를 가로채고자 하는 파라미터 타입에 연결함으로써, 특정 파라미터 타입에 대해 메서드 타입의 interception을 제한할 수 있습니다:
1@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") 2public void beforeSampleMethod(MyType param) { 3 // Advice implementation 4}
1@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") 2fun beforeSampleMethod(param: MyType) { 3 // Advice implementation 4}
이 접근 방식은 generic collection에 대해서는 동작하지 않습니다. 따라서 다음과 같이 pointcut을 정의할 수 없습니다:
1@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") 2public void beforeSampleMethod(Collection<MyType> param) { 3 // Advice implementation 4}
1@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") 2fun beforeSampleMethod(param: Collection<MyType>) { 3 // Advice implementation 4}
이를 동작하게 만들기 위해서는 collection의 모든 element를 검사해야 하는데, 이는 일반적으로 null 값을 어떻게 처리할지 결정할 수 없기 때문에 타당하지 않습니다.
이와 유사한 것을 달성하기 위해서는 파라미터 타입을 Collection<?>로 지정하고, element들의 타입을 수동으로 검사해야 합니다.
advice 호출에서의 파라미터 binding은 pointcut expression에서 사용된 이름을 advice 및 pointcut 메서드 시그니처에 선언된 파라미터 이름과 매칭하는 것에 의존합니다.
이 섹션에서는 AspectJ API가 파라미터 이름을 인수 이름이라고 부르기 때문에, argument 와<br>_parameter_라는 용어를 혼용합니다.
Spring AOP는 파라미터 이름을 결정하기 위해 다음 ParameterNameDiscoverer 구현체들을 사용합니다.
각 discoverer는 파라미터 이름을 발견할 기회를 가지며, 가장 먼저 성공한 discoverer가 사용됩니다.
등록된 discoverer들 중 어떤 것도 파라미터 이름을 결정할 수 없으면, exception이 던져집니다.
AspectJAnnotationParameterNameDiscoverer
해당 advice 또는 pointcut 어노테이션의 argNames attribute를 통해 사용자가 명시적으로 지정한
파라미터 이름을 사용합니다. 자세한 내용은 Explicit Argument Names를 참조하십시오.
KotlinReflectionParameterNameDiscoverer
Kotlin reflection API를 사용하여
파라미터 이름을 결정합니다. 이 discoverer는 해당 API가 classpath에 존재하는 경우에만 사용됩니다.
StandardReflectionParameterNameDiscoverer
표준 java.lang.reflect.Parameter
API를 사용하여 파라미터 이름을 결정합니다. javac에 대해 -parameters
flag로 compile되어야 합니다. Java 8+에서 권장되는 접근 방식입니다.
AspectJAdviceParameterNameDiscoverer
pointcut expression, returning, throwing 절로부터 파라미터 이름을 유추합니다.
사용된 algorithm에 대한 자세한 내용은
javadoc을 참조하십시오.
@AspectJ advice 및 pointcut 어노테이션에는 annotated 메서드의 인수 이름을 지정하는 데 사용할 수 있는 선택적 argNames attribute가 있습니다.
@AspectJaspect가 debug 정보 없이라도 AspectJ compiler(ajc)에 의해 compile된 경우,<br>compiler가 필요한 정보를 유지하므로argNamesattribute를 추가할 필요가 없습니다.<br>유사하게,@AspectJaspect가-parametersflag를 사용하여javac로 compile된 경우에도,<br>compiler가 필요한 정보를 유지하므로argNamesattribute를 추가할 필요가 없습니다.
다음 예시는 argNames attribute를 사용하는 방법을 보여줍니다:
1@Before( 2 value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // (1) 3 argNames = "bean,auditable") // (2) 4public void audit(Object bean, Auditable auditable) { 5 AuditCode code = auditable.value(); 6 // ... use code and bean 7}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
| 2 | bean과 auditable을 인수 이름으로 선언합니다. |
1@Before( 2 value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // (1) 3 argNames = "bean,auditable") // (2) 4fun audit(bean: Any, auditable: Auditable) { 5 val code = auditable.value() 6 // ... use code and bean 7}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
| 2 | bean과 auditable을 인수 이름으로 선언합니다. |
첫 번째 파라미터가 JoinPoint, ProceedingJoinPoint, 또는
JoinPoint.StaticPart 타입인 경우, argNames attribute의 값에서 해당 파라미터 이름을 생략할 수 있습니다.
예를 들어, 앞의 advice를 수정하여 join point 객체를 받도록 하는 경우, argNames attribute에 이를 포함할 필요가 없습니다:
1@Before( 2 value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // (1) 3 argNames = "bean,auditable") // (2) 4public void audit(JoinPoint jp, Object bean, Auditable auditable) { 5 AuditCode code = auditable.value(); 6 // ... use code, bean, and jp 7}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
| 2 | bean과 auditable을 인수 이름으로 선언합니다. |
1@Before( 2 value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // (1) 3 argNames = "bean,auditable") // (2) 4fun audit(jp: JoinPoint, bean: Any, auditable: Auditable) { 5 val code = auditable.value() 6 // ... use code, bean, and jp 7}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
| 2 | bean과 auditable을 인수 이름으로 선언합니다. |
JoinPoint, ProceedingJoinPoint, 또는 JoinPoint.StaticPart 타입의 첫 번째 파라미터에 대해 제공되는 특별한 처리는, 다른 join point context를 수집하지 않는 advice 메서드에 특히 편리합니다.
이러한 상황에서는 argNames attribute를 생략할 수 있습니다.
예를 들어, 다음 advice는 argNames attribute를 선언할 필요가 없습니다:
1@Before("com.xyz.Pointcuts.publicMethod()") // (1) 2public void audit(JoinPoint jp) { 3 // ... use jp 4}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
1@Before("com.xyz.Pointcuts.publicMethod()") // (1) 2fun audit(jp: JoinPoint) { 3 // ... use jp 4}
| 1 | Combining Pointcut Expressions에 정의된 publicMethod named pointcut을 참조합니다. |
앞에서 Spring AOP와 AspectJ 전반에 걸쳐 일관되게 동작하는 인수를 가진 proceed 호출을 작성하는 방법을 설명하겠다고 언급했습니다.
해결책은 advice 시그니처가 메서드 파라미터 각각을 순서대로 binding하도록 하는 것입니다.
다음 예시는 이를 수행하는 방법을 보여줍니다:
1@Around("execution(List<Account> find*(..)) && " + 2 "com.xyz.CommonPointcuts.inDataAccessLayer() && " + 3 "args(accountHolderNamePattern)") // (1) 4public Object preProcessQueryPattern(ProceedingJoinPoint pjp, 5 String accountHolderNamePattern) throws Throwable { 6 String newPattern = preProcess(accountHolderNamePattern); 7 return pjp.proceed(new Object[] {newPattern}); 8}
| 1 | Sharing Named Pointcut Definitions에 정의된 inDataAccessLayer named pointcut을 참조합니다. |
1@Around("execution(List<Account> find*(..)) && " + 2 "com.xyz.CommonPointcuts.inDataAccessLayer() && " + 3 "args(accountHolderNamePattern)") // (1) 4fun preProcessQueryPattern(pjp: ProceedingJoinPoint, 5 accountHolderNamePattern: String): Any? { 6 val newPattern = preProcess(accountHolderNamePattern) 7 return pjp.proceed(arrayOf<Any>(newPattern)) 8}
| 1 | Sharing Named Pointcut Definitions에 정의된 inDataAccessLayer named pointcut을 참조합니다. |
많은 경우, (앞의 예시처럼) 어차피 이러한 binding을 수행하게 됩니다.
여러 개의 advice가 모두 동일한 join point에서 실행되기를 원할 때는 어떻게 될까요? Spring AOP는 advice 실행 순서를 결정하기 위해 AspectJ와 동일한 우선순위 규칙을 따릅니다. 가장 높은 우선순위의 advice가 "진입 시" 먼저 실행됩니다(따라서 두 개의 before advice가 있을 때, 더 높은 우선순위를 가진 것이 먼저 실행됩니다).
join point에서 "빠져나올 때", 가장 높은 우선순위의 advice가 마지막에 실행됩니다(따라서 두 개의 after advice가 있을 때, 더 높은 우선순위를 가진 것이 두 번째로 실행됩니다).
서로 다른 aspect에 정의된 두 개의 advice가 모두 동일한 join point에서 실행되어야 할 때, 별도로 지정하지 않으면 실행 순서는 정의되지 않습니다.
우선순위를 지정하여 실행 순서를 제어할 수 있습니다.
이는 aspect 클래스에서 org.springframework.core.Ordered 인터페이스를 구현하거나 @Order 어노테이션을 붙이는 일반적인 Spring 방식으로 수행됩니다.
두 개의 aspect가 있을 때, Ordered.getOrder()(또는 어노테이션 값)에서 더 낮은 값을 return하는 aspect가 더 높은 우선순위를 가집니다.
특정 aspect의 각 advice 타입은 개념적으로 join point에 직접 적용되도록 의도되어 있습니다.<br>그 결과,
@AfterThrowingadvice 메서드는 동반되는@After/@AfterReturning메서드에서 발생한<br>exception이 아니라 join point(사용자가 선언한 target 메서드) 자체에서 발생한 exception만을 받도록<br>되어 있습니다.<br>동일한 join point에서 실행되어야 하는 동일@Aspect클래스에 정의된 advice 메서드들은 다음과 같은<br>순서(높은 우선순위에서 낮은 우선순위 순)로 advice 타입을 기준으로 우선순위가 할당됩니다:<br>@Around,@Before,@After,@AfterReturning,@AfterThrowing.<br>그러나 AspectJ의@After에 대한 "after finally advice" semantics에 따라, 동일 aspect 내에서<br>어떤@AfterReturning또는@AfterThrowingadvice 메서드 이후에@Afteradvice 메서드가<br>실질적으로 호출된다는 점에 유의하십시오.<br>동일@Aspect클래스에 정의된 동일 타입의 advice(예: 두 개의@Afteradvice 메서드)가 모두 동일한<br>join point에서 실행되어야 할 때, (javac로 compile된 클래스에 대해 reflection을 통해 source code 선언<br>순서를 가져올 수 있는 방법이 없기 때문에) 순서는 정의되지 않습니다.<br>각@Aspect클래스에서 join point당 하나의 advice 메서드로 이러한 advice 메서드들을 통합하거나,<br>advice 조각들을Ordered또는@Order를 통해 aspect 수준에서 순서를 지정할 수 있는 별도의<br>@Aspect클래스로 리팩터링하는 것을 고려하십시오.
Declaring a Pointcut
Introductions