티스토리 뷰

Spring

[Spring] AOP Pointcut execution

hyuuny 2022. 5. 31. 23:29

포인트컷 지시자

포인트컷 표현식은 execution같은 포인트컷 지시자(Pointcut Designator)로 시작한다. 줄여서 PCD라 한다.


포인트컷 지시자 종류

  • execution : 메소드 실행 조인 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하고, 기능도 복잡하다.
  • within : 특정 타입 내의 조인 포인트를 매칭한다.
  • args : 인자가 주어진 타입의 인스턴스인 조인 포인트
  • this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
  • target : Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트
  • @target : 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
  • @within : 주어진 애노테이션이 있는 타입 내 조인 포인트
  • @annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
  • @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
  • bean : 스프링 전용 포인트컷 지시자, 빈의 이름으로 포인트컷을 지정한다.

여러 지시자 중, execution을 가장 많이 사용하고, 나머지는 자주 사용하지 않는다. 따라서 execution을 중점적으로 알아보자!


execution

  AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();

AspectJExpressionPointcut은 포인트컷 표현식을 처리해주는 클래스로써, 여기에 포인트컷 표현식을 지정하면 된다. AspectJExpressionPointcut는 상위에 Pointcut 인터페이스를 가진다.


문법

execution(접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?)
  • 메소드 실행 조인 포인트를 매칭한다.
  • ?는 생략할 수 있다.
  • *같은 패턴을 지정할 수 있다.

가장 정확하게 매칭되는 표현식

  @Test
  void exactMatch() {
    // helloMethod=public java.lang.String com.hyuuny.advanced.member.MemberServiceImpl.hello(java.lang.String)
    pointcut.setExpression("execution(public String com.hyuuny.advanced.member.MemberServiceImpl.hello(String))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

AspectJExpressionPointcutpointcut.setExpression을 통해서 포인트컷 표현식을 적용할 수 있다.
pointcut.matches(메서드,대상 클래스)를 실행하면, 지정한 포인트컷 표현식의 매칭 여부를 true, false로 반환한다.


매칭 조건

  • 접근제어자?: public
  • 반환타입: String
  • 선언타입?: com.hyuuny.advanced.member.MemberServiceImpl
  • 메서드이름: hello
  • 파라미터: (String)
  • 예외?: 생략

가장 많이 생략한 포인트컷

  @Test
  void allMatch() {
    pointcut.setExpression("execution(* *(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

가장 많이 생략한 포인트컷이다.


매칭 조건

  • 접근제어자?: 생략
  • 반환타입: *
  • 선언타입?: 생략
  • 메서드이름: *
  • 파라미터: (..)
  • 예외?: 없음

패키지 매칭 관련 포인트컷

  @Test
  void packageExactMatch1() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.MemberServiceImpl.hello(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void packageExactMatch2() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.*.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void packageExactMatchFalse() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.*.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isFalse();
  }

  @Test
  void packageMatchSubPackage1() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member..*.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void packageMatchSubPackage2() {
    pointcut.setExpression("execution(* com.hyuuny..*.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }
  • com.hyuuny.advanced.member.*(1).*(2) : (1)타입, (2)메서드 이름
  • . : 정확하게 해당 위치의 패키지
  • .. : 해당 위치의 패키지와 그 하위 패키지도 포함


타입 매칭 - 부모 타입 허용

  @Test
  void typeExactMatch() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.MemberServiceImpl.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void typeMatchSuperType() {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.MemberService.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

typeExactMatch() 는 타입 정보가 정확하게 일치하기 때문에 매칭된다.
execution에서는 MemberService처럼 부모 타입을 선언해도 그 자식 타입은 매칭된다.
다형성에서 부모타입 = 자식타입이 할당 가능하다는 점을 떠올려보면 된다.



타입 매칭 - 부모 타입에 있는 메서드만 허용

  @Test
  void typeMatchInternal() throws NoSuchMethodException {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.MemberServiceImpl.*(..))");
    Method internalMethod = MemberServiceImpl.class.getMethod("internal", String.class);
    assertThat(pointcut.matches(internalMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void typeMatchNoSuperTypeMethodFalse() throws NoSuchMethodException {
    pointcut.setExpression("execution(* com.hyuuny.advanced.member.MemberService.*(..))");
    Method internalMethod = MemberServiceImpl.class.getMethod("internal", String.class);
    assertThat(pointcut.matches(internalMethod, MemberServiceImpl.class)).isFalse();
  }

internal(String)메서드는 MemberServiceImpl에만 선언되어 있기 때문에 typeMatchInternal()true를 반환하지만, 부모 타입을 선언한 typeMatchNoSuperTypeMethodFalsefalse를 반환한다.



파라미터 매칭

  // String 타입 파라미터 허용
  // (String)
  @Test
  void argsMatch() {
    pointcut.setExpression("execution(* *(String))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  @Test
  void argsMatchNoArgs() {
    pointcut.setExpression("execution(* *())");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isFalse();
  }

  // 정확히 하나의 파라미터 허용, 모든 타입 허용
  // (Xxx)
  @Test
  void argsMatchStar() {
    pointcut.setExpression("execution(* *(*))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  // 숫자와 무관하게 모든 파라미터, 모든 타입 허용
  // (), (Xxx), (Xxx, Xxx)
  @Test
  void argsMatchAll() {
    pointcut.setExpression("execution(* *(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }

  // String 타입으로 시작, 숫자와 무관하게 모든 파라미터, 모든 타입 허용
  // (String), (String, Xxx), (String, Xxx, Xxx)
  @Test
  void argsMatchComplex() {
    pointcut.setExpression("execution(* *(String, ..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
  }
  • (String) : 정확하게 String 타입 파라미터
  • () : 파라미터가 없어야 한다.
  • (*) : 정확히 하나의 파라미터, 단 모든 타입을 허용한다.
  • (*, *) : 정확히 두 개의 파라미터, 단 모든 타입을 허용한다.
  • (..) : 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다. 참고로 파라미터가 없어도 된다. 0..*로 이해하면 된다.
  • (String, ..) : String 타입으로 시작해야 한다. 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다.
    • (String), (String, Xxx), (String, Xxx, Xxx) 허용


Reference
김영한. 스프링 핵심 원리 - 고급편. 인프런.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8#

'Spring' 카테고리의 다른 글

[Spring Security] JWT (Json Web Token)  (0) 2022.07.21
[Spring] AOP @target, @within  (0) 2022.06.06
[Spring] Advice 종류  (0) 2022.05.29
[Spring] AOP 용어  (0) 2022.05.25
[Spring] AOP는 어떻게 적용되는걸까?  (0) 2022.05.24
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함