스프링 핵심 원리(고급편)

Ch11. 스프링 AOP(포인트컷) - within, args

webmaster 2022. 4. 20. 14:21
728x90

Within

within 지시자는 특정 타입 내의 조인 포인트에 대한 매칭을 제한한다. 쉽게 이야기해서 해당 타입이 매칭 되면 그 안의 메서드(조인 포인트)들이 자동으로 매칭 된다. 문법은 단순한데 execution에서 타입 부분만 사용한다고 보면 된다

Test

@Test
public void withinExact(){
    pointcut.setExpression("within(hello.aop.member.MemberServiceImpl)");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}

@Test
public void withinStar(){
    pointcut.setExpression("within(hello.aop.member.*Service*)");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}

@Test
public void withinSubPackage(){
    pointcut.setExpression("within(hello.aop..*)");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}

within 사용시 주의해야 할 점이 있다. 표현식에 부모 타입을 지정하면 안 된다는 점이다. 정확하게 타입이 맞아야 한다. 이 부분에서 execution과 차이가 난다.

@Test
@DisplayName("타겟의 타입에만 직접 적용, 인터페이스를 선정하면 안된다")
public void withSuperTypeFalse(){
    pointcut.setExpression("within(hello.aop.member.MemberService)");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isFalse();
}

@Test
@DisplayName("타겟의 타입에만 직접 적용, 인터페이스를 선정 가능")
public void executionSuperTypeTrue(){
    pointcut.setExpression("execution(* hello.aop.member.MemberService.*(..))");
    assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}

부모 타입(여기서는 MemberService 인터페이스) 지정시 within 은 실패하고, execution 은 성공하는 것을 확인할 수 있다.

Args

args : 인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭

기본 문법은 execution 의 args 부분과 같다.

차이점 

  • execution 은 파라미터 타입이 정확하게 매칭 되어야 한다. execution 은 클래스에 선언된 정보를 기반으로 판단한다.
  • args 는 부모 타입을 허용한다. args는 실제 넘어온 파라미터 객체 인스턴스를 보고 판단한다

Test

private AspectJExpressionPointcut pointcut(String expression) {
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression(expression);
    return pointcut;
}
@Test
public void args() {
    //hello(String)과 메칭
    assertThat(pointcut("args(String)").matches(helloMethod, MemberServiceImpl.class)).isTrue();
    assertThat(pointcut("args(Object)").matches(helloMethod, MemberServiceImpl.class)).isTrue();
    assertThat(pointcut("args()").matches(helloMethod, MemberServiceImpl.class)).isFalse();
    assertThat(pointcut("args(..)").matches(helloMethod, MemberServiceImpl.class)).isTrue();
    assertThat(pointcut("args(*)").matches(helloMethod, MemberServiceImpl.class)).isTrue();
    assertThat(
        pointcut("args(String, ..)").matches(helloMethod, MemberServiceImpl.class)).isTrue();

}
  • pointcut() : AspectJExpressionPointcut 에 포인트 컷은 한 번만 지정할 수 있다
    • 메서드로 추출해서 사용

Execution VS Args 비교 Test

/**
 * execution(* *(java.io.Serializable)): 메서드의 시그니처로 판단 (정적) args(java.io.Serializable): 런타임에 전달된
 * 인수로 판단 (동적)
 */
@Test
public 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();

}
  • 자바가 기본으로 제공하는 String 은 Object , java.io.Serializable의 하위 타입이다.
  • 정적으로 클래스에 선언된 정보만 보고 판단하는 execution(* *(Object)) 는 매칭에 실패한다
  • 동적으로 실제 파라미터로 넘어온 객체 인스턴스로 판단하는 args(Object) 는 매칭에 성공한다. (부모 타입 허용)
  • 참고
    • Args 지시자는 단독으로 사용되기 보다는 파라미터 바인딩에서 주로 사용된다.
728x90