포인트컷 표현식은 execution같은 포인트컷 지시자(Pointcut Defignator)로 시작한다. 줄여서 PCD라고 한다.
-> excution이 제일 많이 사용되니 ,excution을 중점적으로 이해하기.
[실습]
Annotation
service, serviceImpl
test 1
포인트컷과, 메서드 정보 지정
execution(modifiers-pattern? ret-type-pattern
delcaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution(접근제어자 ? 반환타입 선언타입?메서드이름(파라미터) 예외?)
// ? 는 생략할 수 있다. *과 같은 패턴을 지정할 수 있다.
within 지시자는 특정 타입 내의 조인 포인트들로 매칭을 제한한다.
within 사용시 표현식에 부모타입을 지정하면 안된다. 정확하게 타입이 맞아야 한다. ( excution 과의 차이점)
인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭, 기본 문법은 execution의 args 의 부분과 같다.
@Target, @within은 다음과 같이 애노테이션으로 AOP적용 여부를 판단한다
@ClassAop
class Target{}
-> parentMethod()는 Parent 클래스에만 정의되어 있고, Child 클래스에 정의되어 있지 않기 때문에 @within에서 AOP 적용 대상이 되지 않는다.
주의 : args, @args, @target 은 단독으로 사용하지 않는 포인트컷 지시자이다.
args, @args, @target는 실제 인스턴스가 생성되고 실행될 때 어드바이스 적용 여부를 확인할 수 있다.
실행 시점에 일어나는 포인트컷 적용 여부도 결국 프록시가 있어야 실행 시점에 판단할 수 있다. 프록시가 없다면 판단 자체가 불가능 하다.
그런데 스프링 컨테이너가 프록시를 생성하는 시점은 스프링 컨테이너가 만들어지는 애플리케이션 로딩 시점에 적용할 수 있다. 따라서 args, @args, @target같은 포인트컷 지시자가 없으면 실행 시점에 판단 자체가 불가능하다.
@annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
-> @annotation(hello.aop.member.annotation.MethodAop)
@args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
전달된 인수의 런타임에 @Check 애노테이션이 있는 경우에 매칭한다,
@args(test.Check)
this, target, args, @target, @within, @annotation, @args 포인트컷 표현식을 통해 어드바이스에 매개변수를 전달할 수 있다.
this(hello.aop.member.MemberService)
target(hello.aop.member.MemberService)
this와 target의 차이
스프링 AOP를 적용하면 실제 target 객체 대신에 프록시 객체가 스프링 빈으로 등록된다.
-> this는 스프링 빈으로 등록되어 있는 프록시 객체를 대상으로 포인트 컷을 매칭
-> target은 실제 target 객체를 대상으로 포인트컷을 매칭
프록시 생성 방식에 따른 차이
1. JDK 동적 프록시(인터페이스가 필수, 인터페이스를 구현한 프록시 객체)
JDK 동적 프록시를 적용했을때 this, target
this(hello.aop.member.MemberService)
// proxy 객체를 보고 판단한다. this는 부모 타입을 허용하기 때문에 AOP가 적용된다.
target(hello.aop.member.MemberService)
// target 객체를 보고 판단한다. target은 부모 타입을 허용하기 때문에 AOP가 적용된다.
this(hello.aop.member.MemberServiceImpl)
// proxy 객체를 보고 판단한다.
// JDK 동적 프록시로 만들어진 proxy 객체는 MemberService 인터페이스를 기반으로 구현된 클래스이다.
// 따라서 MemberServiceImpl를 전혀 알지 못하므로 AOP 대상이 아니다.
target(hello.aop.member.MemberServiceImpl)
// target 객체를 보고 판단한다.
// target 객체가 MemberServiceImpl 타입이므로 AOP 적용 대상이다.
this(hello.aop.member.MemberService)
// proxy 객체를 보고 판단한다. this는 부모 타입을 허용하기 때문에 AOP가 적용된다.
target(hello.aop.member.MemberService)
// target 객체를 보고 판단한다. target은 부모 타입을 허용하기 때문에 AOP가 적용된다.
this(hello.aop.member.MemberServiceImpl)
// proxy 객체를 보고 판단한다.
// CGLIB로 만들어진 proxy 객체는 MemberServiceImpl을 상속받아서 만들었기 때문에 AOP가 적용
// this가 부모 타입을 허용하기 때문에 포인트컷의 대상이 된다.
target(hello.aop.member.MemberServiceImpl)
// target 객체를 보고 판단한다.
// target 객체가 MemberServiceImpl 타입이므로 AOP 적용 대상이다.
-> 정리 : 프록시를 대상으로 하는 this의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수도 있다.
JDK 동적프록시 우선으로
spring.aop.proxy-target-class=false
CGLIB 우선으로 테스트
spring.aop.proxy-target-class=true