[review] 스프링 AOP - 포인트컷

corncheese·2023년 11월 15일
0

포인트컷 지시자

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

포인트컷 지시자의 종류

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

-> excution이 제일 많이 사용되니 ,excution을 중점적으로 이해하기.

[실습]

  • Annotation

  • service, serviceImpl

  • test 1
    포인트컷과, 메서드 정보 지정

excution 문법

execution(modifiers-pattern? ret-type-pattern 
delcaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

execution(접근제어자 ? 반환타입 선언타입?메서드이름(파라미터) 예외?)
//  ? 는 생략할 수 있다. *과 같은 패턴을 지정할 수 있다.

메서드 이름 매칭 관련 포인트컷

패키지 매칭 관련 포인트컷

타입 매칭 - 부모 타입 허용

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

파라미터 매칭

within

within 지시자는 특정 타입 내의 조인 포인트들로 매칭을 제한한다.


within 사용시 표현식에 부모타입을 지정하면 안된다. 정확하게 타입이 맞아야 한다. ( excution 과의 차이점)

args

인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭, 기본 문법은 execution의 args 의 부분과 같다.

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

@Target, @within

  • @Target : 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
    인스턴스의 모든 메서드를 조인 포인트로 적용한다.
    -> 부모 클래스의 메서드까지 어드바이스를 다 적용함
  • @within : 주어진 애노테이션이 있는 타입 내 조인 포인트
    해당 타입 내에 있는 메서드만 조인 포인트로 적용한다.
    -> 자기 자신의 클래스에 정의된 메서드에만 어드바이스를 적용함

@Target, @within은 다음과 같이 애노테이션으로 AOP적용 여부를 판단한다

  • @Target(hello.aop.member.annotation.ClassAop)
  • @within(hello.aop.member.annotation.ClassAop)
@ClassAop
class Target{}

-> parentMethod()는 Parent 클래스에만 정의되어 있고, Child 클래스에 정의되어 있지 않기 때문에 @within에서 AOP 적용 대상이 되지 않는다.

주의 : args, @args, @target 은 단독으로 사용하지 않는 포인트컷 지시자이다.
args, @args, @target는 실제 인스턴스가 생성되고 실행될 때 어드바이스 적용 여부를 확인할 수 있다.
실행 시점에 일어나는 포인트컷 적용 여부도 결국 프록시가 있어야 실행 시점에 판단할 수 있다. 프록시가 없다면 판단 자체가 불가능 하다.
그런데 스프링 컨테이너가 프록시를 생성하는 시점은 스프링 컨테이너가 만들어지는 애플리케이션 로딩 시점에 적용할 수 있다. 따라서 args, @args, @target같은 포인트컷 지시자가 없으면 실행 시점에 판단 자체가 불가능하다.

@annotation, @args

  • @annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
    -> @annotation(hello.aop.member.annotation.MethodAop)

  • @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
    전달된 인수의 런타임에 @Check 애노테이션이 있는 경우에 매칭한다,
    @args(test.Check)

bean

  • bean : 스프링 전용 포인트컷 지시자, 빈의 이름으로 지정한다.
  • 스프링 빈의 이름으로 AOP 적용 여부를 지정한다. 스프링에서만 사용할 수 있는 특별한 지시자이다.
  • bean(orderService) || bean(*Repository)

매개변수 전달

this, target, args, @target, @within, @annotation, @args 포인트컷 표현식을 통해 어드바이스에 매개변수를 전달할 수 있다.

this, target

  • this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
  • target : Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)을 대상으로 하는 조인 포인트
  • this, target 은 다음과 같이 적용 타입 하나를 정확하게 지정해야 한다.
this(hello.aop.member.MemberService)
target(hello.aop.member.MemberService)

this와 target의 차이
스프링 AOP를 적용하면 실제 target 객체 대신에 프록시 객체가 스프링 빈으로 등록된다.
-> this는 스프링 빈으로 등록되어 있는 프록시 객체를 대상으로 포인트 컷을 매칭
-> target은 실제 target 객체를 대상으로 포인트컷을 매칭

프록시 생성 방식에 따른 차이
1. JDK 동적 프록시(인터페이스가 필수, 인터페이스를 구현한 프록시 객체)

JDK 동적 프록시를 적용했을때 this, target

  • MemberService 인터페이스 지정
this(hello.aop.member.MemberService)
// proxy 객체를 보고 판단한다. this는 부모 타입을 허용하기 때문에 AOP가 적용된다.

target(hello.aop.member.MemberService)
// target 객체를 보고 판단한다. target은 부모 타입을 허용하기 때문에 AOP가 적용된다.
  • MemberServiceImpl 구체 클래스 지정
this(hello.aop.member.MemberServiceImpl)
// proxy 객체를 보고 판단한다. 
// JDK 동적 프록시로 만들어진 proxy 객체는 MemberService 인터페이스를 기반으로 구현된 클래스이다.
// 따라서 MemberServiceImpl를 전혀 알지 못하므로 AOP 대상이 아니다.

target(hello.aop.member.MemberServiceImpl)
// target 객체를 보고 판단한다.
// target 객체가 MemberServiceImpl 타입이므로 AOP 적용 대상이다.
  1. CBLIB 프록시(클래스 기반 프록시 객체)
  • MemberService 인터페이스 지정
this(hello.aop.member.MemberService)
// proxy 객체를 보고 판단한다. this는 부모 타입을 허용하기 때문에 AOP가 적용된다.

target(hello.aop.member.MemberService)
// target 객체를 보고 판단한다. target은 부모 타입을 허용하기 때문에 AOP가 적용된다.
  • MemberServiceImpl 구체 클래스 지정
this(hello.aop.member.MemberServiceImpl)
// proxy 객체를 보고 판단한다. 
// CGLIB로 만들어진 proxy 객체는 MemberServiceImpl을 상속받아서 만들었기 때문에 AOP가 적용
// this가 부모 타입을 허용하기 때문에 포인트컷의 대상이 된다.

target(hello.aop.member.MemberServiceImpl)
// target 객체를 보고 판단한다.
// target 객체가 MemberServiceImpl 타입이므로 AOP 적용 대상이다.

-> 정리 : 프록시를 대상으로 하는 this의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수도 있다.

  1. JDK 동적프록시 우선으로
    spring.aop.proxy-target-class=false

  2. CGLIB 우선으로 테스트
    spring.aop.proxy-target-class=true

0개의 댓글