Spring Boot
Spring AOP - 포인트컷(pointcut) 지시자(within, args)
작은별._.
2024. 10. 2. 21:30
728x90
해당 포스팅에 이어서 포인트컷 지시자에 대해서 작성하였습니다.
within
within 지시자는 특정 타입 내의 조인 포인트들로 매칭을 제한합니다. 즉, 해당 타입이 매칭되면 그 안의 메서드(조인 포인트)들이 자동으로 매칭됩니다.
문법은 execution에서 타입 부분만 사용한다고 보면 됩니다. 참고로 execution 문법은 아래와 같았습니다.
execution([접근제어자?] [메서드 반환타입] [선언 타입?][메서드 이름]([파라미터]) [예외?])
즉, 여기에서 [선언 타입?] 부분을 사용합니다. 즉, within(패키지명..*)
지난 ExecutionTest 에 이어서 WithinTest 코드를 작성해 보겠습니다.
public class WithinTest {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
Method helloMethod;
@BeforeEach
public void init() throws NoSuchMethodException {
// hello 이름을 가진 메소드 가지고 옵니다.
helloMethod = MemberServiceImpl.class.getMethod("hello", String.class);
}
@Test
void withinExact() {
// 정확한 표현식
pointcut.setExpression("within(hello.aop.member.MemberServiceImpl)");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
@Test
void withinStar() {
// Service 이름이 들어간 클래스
pointcut.setExpression("within(hello.aop.member.*Service*)");
assertThat(pointcut.matches(helloMethod,
MemberServiceImpl.class)).isTrue();
}
@Test
void withinSubPackage() {
// hello.aop 패키지와 하위 패키지들 포함
pointcut.setExpression("within(hello.aop..*)");
assertThat(pointcut.matches(helloMethod,
MemberServiceImpl.class)).isTrue();
}
@Test
void withinSuperTypeFalse() {
// within은 부모 타입을 지정하면 X, 정확하게 타입이 맞아야 합니다.
pointcut.setExpression("within(hello.aop.member.MemberService)");
assertThat(pointcut.matches(helloMethod,
MemberServiceImpl.class)).isFalse(); // False!!
}
@Test
void executionSuperTypeTrue() {
// execution은 부모 타입을 지정해도 가능합니다.
pointcut.setExpression("execution(* hello.aop.member.MemberService.*(..))");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
}
마지막 2개 테스트에서 확인할 수 있듯이, execution과 달리 within 표현식에는 부모 타입이 아닌 정확한 타입을 표현해 주어야 합니다.
args
args 는 인자가 주어진 타입의 인스턴스인 Joinpoint로 매칭합니다. 기본 문법은 execution의 매개변수 부분과 같습니다. 차이점은, execution은 파라미터 타입이 정확하게 매칭되어야 하지만, args는 부모 타입을 허용합니다. 코드를 통해 확인해 보겠습니다.
package hello.aop.member;
@Component
public class MemberServiceImpl implements MemberService {
@Override
@MethodAop("test value")
public String hello(String param) {
return "ok";
}
public String internal(String param) {
return "ok";
}
}
public class ArgsTest {
Method helloMethod;
@BeforeEach
public void init() throws NoSuchMethodException {
helloMethod = MemberServiceImpl.class.getMethod("hello", String.class);
}
private AspectJExpressionPointcut pointcut(String expression) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
return pointcut;
}
@Test
void args() {
//hello(String)과 매칭
assertThat(pointcut("args(String)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
// Object은 String의 부모 타입으로 args로는 hello(String) 과 매칭 가능
assertThat(pointcut("args(Object)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
// 파라미터가 없으므로(void) hello(String)과 매칭 X
assertThat(pointcut("args()")
.matches(helloMethod, MemberServiceImpl.class)).isFalse();
// 파라미터가 (..)로, 어떤 타입의 파라미터가 몇 개여도 상관없다는 의미
// -> hello(String)과 매칭 가능
assertThat(pointcut("args(..)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
// * 는 모든 파라미터 허용하므로 hello(String)과 매칭 가능
assertThat(pointcut("args(*)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
assertThat(pointcut("args(String,..)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
/**
* execution(* *(java.io.Serializable)): 메서드의 시그니처로 판단 (정적)
* args(java.io.Serializable): 런타임에 전달된 인수로 판단 (동적)
*/
@Test
void argsVsExecution() {
//Args
assertThat(pointcut("args(String)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
assertThat(pointcut("args(java.io.Serializable)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
assertThat(pointcut("args(Object)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
//Execution
assertThat(pointcut("execution(* *(String))")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();
assertThat(pointcut("execution(* *(java.io.Serializable))") //매칭 실패
.matches(helloMethod, MemberServiceImpl.class)).isFalse();
assertThat(pointcut("execution(* *(Object))") //매칭 실패
.matches(helloMethod, MemberServiceImpl.class)).isFalse();
}
}
- 정적으로 클래스에 선언된 정보만 보고 판단하는 execution(* *(Object)) 는 매칭에 실패합니다.
- 동적으로 실제 파라미터로 넘어온 객체 인스턴스로 판단하는 args(Object)는 매칭에 성공합니다.
https://github.com/eunhwa99/SpringAOP/tree/main/advanced/src/test/java/hello/aop/pointcut
[참고자료]
김영한, "스프링 핵심 원리 - 고급편 ", 인프런
728x90
반응형