You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
애스펙트J는 포인트컷을 편리하게 표현하기 위한 특별한 표현식을 제공한다.
예) @Pointcut("execution(* hello.aop.order..*(..))")
포인트컷 표현식은 AspectJ pointcut expression 즉 애스펙트J가 제공하는 포인트컷 표현식을 줄여서 말하는 것이다.
포인트컷 지시자
포인트컷 표현식은 execution 같은 포인트컷 지시자(Pointcut Designator)로 시작한다. 줄여서 PCD라 한다.
포인트컷 지시자의 종류
execution : 메소드 실행 조인 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하고, 기능도 복잡하다.
within : 특정 타입 내의 조인 포인트를 매칭한다.
args : 인자가 주어진 타입의 인스턴스인 조인 포인트
this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
target : Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트
@target : 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
@within : 주어진 애노테이션이 있는 타입 내 조인 포인트
@annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
@args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
bean : 스프링 전용 포인트컷 지시자, 빈의 이름으로 포인트컷을 지정한다.
포인트컷 지시자가 무엇을 뜻하는지, 사실 글로만 읽어보면 이해하기 쉽지 않다. 예제를 통해서 하나씩 이해해보자. execution 은 가장 많이 사용하고, 나머지는 자주 사용하지 않는다. 따라서 execution 을 중점적으로 이해하자.
2. 예제 만들기
포인트컷 표현식을 이해하기 위해 예제 코드를 하나 추가하자.
ClassAop
packagehello.aop.member.annotation;
importjava.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAop {
}
AspectJExpressionPointcut 이 바로 포인트컷 표현식을 처리해주는 클래스다. 여기에 포인트컷 표현식을 지정 하면 된다. AspectJExpressionPointcut 는 상위에 Pointcut 인터페이스를 가진다. printMethod() 테스트는 MemberServiceImpl.hello(String) 메서드의 정보를 출력해준다.
실행 결과
helloMethod = public java.lang.String hello.aop.member.MemberServiceImpl.hello(java.lang.String)
이번에 알아볼 execution 으로 시작하는 포인트컷 표현식은 이 메서드 정보를 매칭해서 포인트컷 대상을 찾아낸다.
typeExactMatch() 는 타입 정보가 정확하게 일치하기 때문에 매칭된다. typeMatchSuperType() 을 주의해서 보아야 한다. execution 에서는 MemberService 처럼 부모 타입을 선언해도 그 자식 타입은 매칭된다. 다형성에서 부모타입 = 자식타입 이 할당 가능하다는 점을 떠올려보면 된다.
타입 매칭 - 부모 타입에 있는 메서드만 허용
@TestvoidtypeMatchInternal() throwsNoSuchMethodException {
pointcut.setExpression("execution(* hello.aop.member.MemberServiceImpl.*(..))");
MethodinternalMethod = MemberServiceImpl.class.getMethod("internal", String.class);
assertThat(pointcut.matches(internalMethod, MemberServiceImpl.class)).isTrue();
}
//포인트컷으로 지정한 MemberService 는 internal 이라는 이름의 메서드가 없다. @TestvoidtypeMatchNoSuperTypeMethodFalse() throwsNoSuchMethodException {
pointcut.setExpression("execution(* hello.aop.member.MemberService.*(..))");
MethodinternalMethod = MemberServiceImpl.class.getMethod("internal", String.class);
assertThat(pointcut.matches(internalMethod, MemberServiceImpl.class)).isFalse();
}
typeMatchInternal() 의 경우 MemberServiceImpl 를 표현식에 선언했기 때문에 그 안에 있는 internal(String) 메서드도 매칭 대상이 된다. typeMatchNoSuperTypeMethodFalse() 를 주의해서 보아야 한다.
이 경우 표현식에 부모 타입인 MemberService 를 선언했다. 그런데 자식 타입인 MemberServiceImpl 의 internal(String) 메서드를 매칭하려 한다. 이 경우 매칭에 실패한다. MemberService 에는 internal(String) 메서드가 없다!
부모 타입을 표현식에 선언한 경우 부모 타입에서 선언한 메서드가 자식 타입에 있어야 매칭에 성공한다. 그래서 부모 타입에 있는 hello(String) 메서드는 매칭에 성공하지만, 부모 타입에 없는 internal(String) 는 매칭에 실패한다.
파라미터 매칭
//String 타입의 파라미터 허용 //(String)@TestvoidargsMatch() {
pointcut.setExpression("execution(* *(String))");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
//파라미터가 없어야 함//()@TestvoidargsMatchNoArgs() {
pointcut.setExpression("execution(* *())");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isFalse();
}
//정확히 하나의 파라미터 허용, 모든 타입 허용 //(Xxx)@TestvoidargsMatchStar() {
pointcut.setExpression("execution(* *(*))");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
//숫자와 무관하게 모든 파라미터, 모든 타입 허용 //파라미터가 없어도 됨//(), (Xxx), (Xxx, Xxx)@TestvoidargsMatchAll() {
pointcut.setExpression("execution(* *(..))");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
//String 타입으로 시작, 숫자와 무관하게 모든 파라미터, 모든 타입 허용 //(String), (String, Xxx), (String, Xxx, Xxx) 허용@TestvoidargsMatchComplex() {
pointcut.setExpression("execution(* *(String, ..))");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();
}
execution 파라미터 매칭 규칙은 다음과 같다.
(String) : 정확하게 String 타입 파라미터
() : 파라미터가 없어야 한다.
(*) : 정확히 하나의 파라미터, 단 모든 타입을 허용한다.
(*, *) : 정확히 두 개의 파라미터, 단 모든 타입을 허용한다.
(..) : 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다. 참고로 파라미터가 없어도 된다. 0..* 로 이해하면 된다.
(String, ..) : String 타입으로 시작해야 한다. 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다.
parentMethod() 는 Parent 클래스에만 정의되어 있고, Child 클래스에 정의되어 있지 않기 때문에 @within 에서 AOP 적용 대상이 되지 않는다.
실행결과를 보면 child.parentMethod() 를 호출 했을 때 [@within] 이 호출되지 않은 것을 확인할 수 있다.
참고 @target , @within 지시자는 뒤에서 설명할 파라미터 바인딩에서 함께 사용된다.
주의
다음 포인트컷 지시자는 단독으로 사용하면 안된다. args, @args, @target
이번 예제를 보면 execution(* hello.aop..*(..)) 를 통해 적용 대상을 줄여준 것을 확인할 수 있다. args , @args , @target 은 실제 객체 인스턴스가 생성되고 실행될 때 어드바이스 적용 여부를 확인할 수 있다.
실행 시점에 일어나는 포인트컷 적용 여부도 결국 프록시가 있어야 실행 시점에 판단할 수 있다. 프록시가 없다면 판단 자체가 불가능하다. 그런데 스프링 컨테이너가 프록시를 생성하는 시점은 스프링 컨테이너가 만들어지는 애플리케이션 로딩 시점에 적용할 수 있다. 따라서 args , @args , @target 같은 포인트컷 지시자가 있으면 스프링은 모든 스프링 빈에 AOP를 적용하려고 시도한다. 앞서 설명한 것 처럼 프록시가 없으면 실행 시점에 판단 자체가 불가능하다.
문제는 이렇게 모든 스프링 빈에 AOP 프록시를 적용하려고 하면 스프링이 내부에서 사용하는 빈 중에는 final 로 지정된 빈들도 있기 때문에 오류가 발생할 수 있다.
따라서 이러한 표현식은 최대한 프록시 적용 대상을 축소하는 표현식과 함께 사용해야 한다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
1. 포인트컷 지시자
지금부터 포인트컷 표현식을 포함한 포인트컷에 대해서 자세히 알아보자.
애스펙트J는 포인트컷을 편리하게 표현하기 위한 특별한 표현식을 제공한다.
예)
@Pointcut("execution(* hello.aop.order..*(..))")포인트컷 표현식은 AspectJ pointcut expression 즉 애스펙트J가 제공하는 포인트컷 표현식을 줄여서 말하는 것이다.
포인트컷 지시자
포인트컷 표현식은
execution같은 포인트컷 지시자(Pointcut Designator)로 시작한다. 줄여서 PCD라 한다.execution: 메소드 실행 조인 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하고, 기능도 복잡하다.within: 특정 타입 내의 조인 포인트를 매칭한다.args: 인자가 주어진 타입의 인스턴스인 조인 포인트this: 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트target: Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트@target: 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트@within: 주어진 애노테이션이 있는 타입 내 조인 포인트@annotation: 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭@args: 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트bean: 스프링 전용 포인트컷 지시자, 빈의 이름으로 포인트컷을 지정한다.포인트컷 지시자가 무엇을 뜻하는지, 사실 글로만 읽어보면 이해하기 쉽지 않다. 예제를 통해서 하나씩 이해해보자.
execution은 가장 많이 사용하고, 나머지는 자주 사용하지 않는다. 따라서execution을 중점적으로 이해하자.2. 예제 만들기
포인트컷 표현식을 이해하기 위해 예제 코드를 하나 추가하자.
ClassAop
MethodAop
MemberService
MemberServiceImpl
ExecutionTest
AspectJExpressionPointcut이 바로 포인트컷 표현식을 처리해주는 클래스다. 여기에 포인트컷 표현식을 지정 하면 된다.AspectJExpressionPointcut는 상위에Pointcut인터페이스를 가진다.printMethod()테스트는MemberServiceImpl.hello(String)메서드의 정보를 출력해준다.실행 결과
이번에 알아볼
execution으로 시작하는 포인트컷 표현식은 이 메서드 정보를 매칭해서 포인트컷 대상을 찾아낸다.3. execution1
execution 문법
*같은 패턴을 지정할 수 있다.실제 코드를 하나씩 보면서
execution을 이해해보자.가장 정확한 포인트컷
먼저
MemberServiceImpl.hello(String)메서드와 가장 정확하게 모든 내용이 매칭되는 표현식이다.ExecutionTest - 추가
AspectJExpressionPointcut에pointcut.setExpression을 통해서 포인트컷 표현식을 적용할 수 있다.pointcut.matches(메서드, 대상 클래스)를 실행하면 지정한 포인트컷 표현식의 매칭 여부를true,false로 반환한다.매칭 조건
publicStringhello.aop.member.MemberServiceImplhello(String)MemberServiceImpl.hello(String)메서드와 포인트컷 표현식의 모든 내용이 정확하게 일치한다. 따라서true를 반환한다.가장 많이 생략한 포인트컷
가장 많이 생략한 포인트컷이다.
매칭 조건
접근제어자?: 생략
반환타입:
*선언타입?: 생략
메서드이름:
*파라미터:
(..)예외?: 없음
*은 아무 값이 들어와도 된다는 뜻이다.파라미터에서
..은 파라미터의 타입과 파라미터 수가 상관없다는 뜻이다. (0..*) 파라미터는 뒤에 자세히 정리하겠다.메서드 이름 매칭 관련 포인트컷
메서드 이름 앞뒤에
*을 사용해서 매칭할 수 있다.패키지 매칭 관련 포인트컷
hello.aop.member.*(1).*(2)패키지에서
.,..의 차이를 이해해야 한다..: 정확하게 해당 위치의 패키지..: 해당 위치의 패키지와 그 하위 패키지도 포함4. execution2
타입 매칭 - 부모 타입 허용
typeExactMatch()는 타입 정보가 정확하게 일치하기 때문에 매칭된다.typeMatchSuperType()을 주의해서 보아야 한다.execution에서는MemberService처럼 부모 타입을 선언해도 그 자식 타입은 매칭된다. 다형성에서부모타입 = 자식타입이 할당 가능하다는 점을 떠올려보면 된다.타입 매칭 - 부모 타입에 있는 메서드만 허용
typeMatchInternal()의 경우MemberServiceImpl를 표현식에 선언했기 때문에 그 안에 있는internal(String)메서드도 매칭 대상이 된다.typeMatchNoSuperTypeMethodFalse()를 주의해서 보아야 한다.이 경우 표현식에 부모 타입인
MemberService를 선언했다. 그런데 자식 타입인MemberServiceImpl의internal(String)메서드를 매칭하려 한다. 이 경우 매칭에 실패한다.MemberService에는internal(String)메서드가 없다!부모 타입을 표현식에 선언한 경우 부모 타입에서 선언한 메서드가 자식 타입에 있어야 매칭에 성공한다. 그래서 부모 타입에 있는
hello(String)메서드는 매칭에 성공하지만, 부모 타입에 없는internal(String)는 매칭에 실패한다.파라미터 매칭
execution 파라미터 매칭 규칙은 다음과 같다.
(String): 정확하게 String 타입 파라미터(): 파라미터가 없어야 한다.(*): 정확히 하나의 파라미터, 단 모든 타입을 허용한다.(*, *): 정확히 두 개의 파라미터, 단 모든 타입을 허용한다.(..): 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다. 참고로 파라미터가 없어도 된다.0..*로 이해하면 된다.(String, ..): String 타입으로 시작해야 한다. 숫자와 무관하게 모든 파라미터, 모든 타입을 허용한다.(String),(String, Xxx),(String, Xxx, Xxx)허용5. within
within지시자는 특정 타입 내의 조인 포인트들로 매칭을 제한한다. 쉽게 이야기해서 해당 타입이 매칭되면 그 안의 메서드(조인 포인트)들이 자동으로 매칭된다.문법은 단순한데
execution에서 타입 부분만 사용한다고 보면 된다.WithinTest
코드를 보면 이해하는데 어려움은 없을 것이다.
주의
그런데
within사용시 주의해야 할 점이 있다. 표현식에 부모 타입을 지정하면 안된다는 점이다. 정확하게 타입이 맞아야 한다. 이 부분에서execution과 차이가 난다.WithinTest - 추가
부모 타입(여기서는
MemberService인터페이스) 지정시within은 실패하고,execution은 성공하는 것을 확인할 수 있다.6. args
args: 인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭execution의args부분과 같다.execution과 args의 차이점
execution은 파라미터 타입이 정확하게 매칭되어야 한다.execution은 클래스에 선언된 정보를 기반으로 판단한다.args는 부모 타입을 허용한다.args는 실제 넘어온 파라미터 객체 인스턴스를 보고 판단한다.ArgsTest
pointcut():AspectJExpressionPointcut에 포인트컷은 한번만 지정할 수 있다. 이번 테스트에서는 테스트를 편리하게 진행하기 위해 포인트컷을 여러번 지정하기 위해 포인트컷 자체를 생성하는 메서드를 만들었다.String은Object,java.io.Serializable의 하위 타입이다.execution(* *(Object))는 매칭에 실패한다.args(Object)는 매칭에 성공한다. (부모 타입 허용)참고
args지시자는 단독으로 사용되기 보다는 뒤에서 설명할 파라미터 바인딩에서 주로 사용된다.7. @target, @Within
정의
@target: 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트@within: 주어진 애노테이션이 있는 타입 내 조인 포인트설명
@target,@within은 다음과 같이 타입에 있는 애노테이션으로 AOP 적용 여부를 판단한다.@target(hello.aop.member.annotation.ClassAop)@within(hello.aop.member.annotation.ClassAop)@target vs @Within
@target은 인스턴스의 모든 메서드를 조인 포인트로 적용한다.@within은 해당 타입 내에 있는 메서드만 조인 포인트로 적용한다.쉽게 이야기해서
@target은 부모 클래스의 메서드까지 어드바이스를 다 적용하고,@within은 자기 자신의 클래스 에 정의된 메서드에만 어드바이스를 적용한다.AtTargetAtWithinTest
실행 결과
parentMethod()는Parent클래스에만 정의되어 있고,Child클래스에 정의되어 있지 않기 때문에@within에서 AOP 적용 대상이 되지 않는다.실행결과를 보면
child.parentMethod()를 호출 했을 때[@within]이 호출되지 않은 것을 확인할 수 있다.참고
@target,@within지시자는 뒤에서 설명할 파라미터 바인딩에서 함께 사용된다.주의
다음 포인트컷 지시자는 단독으로 사용하면 안된다.
args, @args, @target이번 예제를 보면
execution(* hello.aop..*(..))를 통해 적용 대상을 줄여준 것을 확인할 수 있다.args,@args,@target은 실제 객체 인스턴스가 생성되고 실행될 때 어드바이스 적용 여부를 확인할 수 있다.실행 시점에 일어나는 포인트컷 적용 여부도 결국 프록시가 있어야 실행 시점에 판단할 수 있다. 프록시가 없다면 판단 자체가 불가능하다. 그런데 스프링 컨테이너가 프록시를 생성하는 시점은 스프링 컨테이너가 만들어지는 애플리케이션 로딩 시점에 적용할 수 있다. 따라서
args,@args,@target같은 포인트컷 지시자가 있으면 스프링은 모든 스프링 빈에 AOP를 적용하려고 시도한다. 앞서 설명한 것 처럼 프록시가 없으면 실행 시점에 판단 자체가 불가능하다.문제는 이렇게 모든 스프링 빈에 AOP 프록시를 적용하려고 하면 스프링이 내부에서 사용하는 빈 중에는
final로 지정된 빈들도 있기 때문에 오류가 발생할 수 있다.따라서 이러한 표현식은 최대한 프록시 적용 대상을 축소하는 표현식과 함께 사용해야 한다.
Beta Was this translation helpful? Give feedback.
All reactions