AspectJ는 Pointcut을 편리하게 표현하기 위한 표현식을 별도로 제공한다.
AspectJ pointcut expression이라고 부르며 간단하게 포인트 컷 표현식이라고 부른다.
이러한 포인트 컷 표현식에는 Pointcut Designator라는 포인트 컷 지시자가 존재하며 예를 들어 execution, within.. 등등이 존재한다.
포인트 컷 지시자의 종류 종류
- execution : 메서드 실행 조인 포인트를 매칭 한다. 스프링 AOP에서 가장 많이 사용하고, 기능도 복잡하다.
- within : 특정 타입 내의 조인 포인트를 매칭 한다.
- args : 인자가 주어진 타입의 인스턴스인 조인 포인트
- this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
- target : Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트
- @target : 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
- @within : 주어진 애노테이션이 있는 타입 내 조인 포인트
- @annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
- @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
- bean : 스프링 전용 포인트 컷 지시자, 빈의 이름으로 포인트 컷을 지정한다.
execution interface
execution(접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?)
* "?"는 생략 가능을 나타낸다.
* 여러 가지 패턴을 적용할 수 있다.
package hello.aop.example;
public interface ExampleInterface {
String test(String param);
}
package hello.aop.example;
public class Example implements ExampleInterface{
@Override
public String test(String param) {
return "test";
}
}
AspectJ표현식을 통해 해당 메서드를 매칭 하는 테스트를 진행해보자.
@Test
void exactMatch() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public String hello.aop.example.Example.test(String))");
Method exampleMethod = Example.class.getMethod("test", String.class);
log.info("exampleMethod = {}", exampleMethod);
assertThat(pointcut.matches(exampleMethod, Example.class)).isTrue();
}
매칭 조건은 다음과 같다.
- 접근제어자 public
- 반환 타입 String
- 선언 타입 패키지와 클래스명(hello.aop.example.Exmaple)
- 메서드 이름 : test
- 파라미터 (String)
- 예외는 없어서 생략했다.
execution의 생략 불가능한 부분은 반환 타입, 메서드 이름, 파라미터로 3가지를 패턴을 통해 매칭 시켜보자.
pointcut.setExpression("execution(* *(..))");
*는 어떠한 값이 들어와도 상관없고, 파라미터의.. 은 파라미터 타입과 파라미터의 수가 상관없이 매칭 된다는 것이다.
이는 가장 많이 생략한 포인트 컷 표현식이다.
보통 패키지에 패턴을 넣어 매칭 하는 부분이 많다.
pointcut.setExpression("execution(* hello.aop.example.*.*(..))");
hello.aop.example패키지 내부 클래스의 존재하는 모든 메서드에 파라미터와 상관없이 모두 매칭 된다.
@Test
void packageExactMatch2() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.aop.*.*(..))");
Method exampleMethod = Example.class.getMethod("test", String.class);
assertThat(pointcut.matches(exampleMethod, Example.class)).isTrue();
}
이 테스트는 실패한다. 그 이유는 Example 클래스의 위치는 hello.aop.example 패키지에 존재하기 때문이다.
그래서 보통 서브 패키지까지 매칭 하기 위해선 다음과 같이 진행한다.
pointcut.setExpression("execution(* hello.aop..*.*(..))");
이는 aop 패키지까지도 포함이 되고 그 하위 모든 패키지들도 대상이 된다.
- "." - "." 은 정확하게 해당 패키지를 의미한다.
- ".." - ".." 은 해당 위치의 패키지와 그 하위 패키지를 포함한다.
Type 매칭
자식 타입이 아닌 부모 타입을 포인트컷 표현식에 지정해도 자식 타입의 메서드가 매칭 된다.
@Test
void typeMatchSuperType() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.aop.example.ExampleInterface.*(..))");
Method exampleMethod = Example.class.getMethod("test", String.class);
assertThat(pointcut.matches(exampleMethod, Example.class)).isTrue();
}
- 생각해보자 Java를 배울 당시 부모 타입으로 캐스팅된 자식 타입 인스턴스를 만들었을 때 자식 타입에만 존재하는 메서드를 사용할 수 있었는가?
그렇지 않다. 부모 타입으로 캐스팅된 자식 타입의 인스턴스는 부모 타입에 존재하는 메서드만 사용이 가능했다. 이는 포인트 컷 매칭과도 같다.
public class Example implements ExampleInterface{
@Override
public String test(String param) {
return "test";
}
public String internal(String param) {
return "internal";
}
}
Example.Class에 internal 메서드를 추가한 후 해당 메서드가 매칭 되는지 테스트해보자.
@Test
void typeMatchNotOverrideMethod() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.aop.example.ExampleInterface.*(..))");
Method exampleMethod = Example.class.getMethod("internal", String.class);
assertThat(pointcut.matches(exampleMethod, Example.class)).isFalse();
}
isFalse()를 통해 테스트가 성공했다.
파라미터 매칭 규칙
- (T) : 정확하게 T 타입 파라미터
pointcut.setExpression("execution(* *(String))");
- () : 파라미터가 없어야 함
pointcut.setExpression("execution(* *())");
- (*) : 정확히 한 개의 파라미터가 존재해야 한다. 단 모든 타입을 허용한다.
pointcut.setExpression("execution(* *(*))");
- (*, *) : 정확히 두 개의 파라미터, 단 모든 타입 허용
pointcut.setExpression("execution(* *(*,*))");
- (..) : 파라미터 개수와 모든 타입을 허용하고 없어도 된다.
pointcut.setExpression("execution(* *(..))");
- (T,..) : T 타입의 파라미터로 시작하고, 숫자와 무관하게 모든 파라미터 모든 타입 허용
pointcut.setExpression("execution(* *(String, ..))");
'Spring|Spring-boot > Spring AOP' 카테고리의 다른 글
Spring AOP @target, @within (0) | 2022.01.29 |
---|---|
Pointcut - within, args (0) | 2022.01.29 |
Spring AOP (2) (0) | 2022.01.29 |
Spring AOP (0) | 2022.01.27 |
[Spring boot] AOP 활용예제 (0) | 2021.06.09 |
댓글