본문 바로가기
Spring|Spring-boot/Spring AOP

Pointcut - execution

by oncerun 2022. 1. 29.
반응형

 

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

댓글