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
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로 지정된 빈들도 있기 때문에 오류가 발생할 수 있다.
따라서 이러한 표현식은 최대한 프록시 적용 대상을 축소하는 표현식과 함께 사용해야 한다.
OrderService , *Repository(OrderRepository) 의 메서드에 AOP가 적용된다. 실행 결과
[bean] void hello.aop.order.OrderService.orderItem(String)
[orderService] 실행
[bean] String hello.aop.order.OrderRepository.save(String)
[orderRepository] 실행
this vs target
단순히 타입 하나를 정하면 되는데, this 와 target 은 어떤 차이가 있을까?
스프링에서 AOP를 적용하면 실제 target 객체 대신에 프록시 객체가 스프링 빈으로 등록된다.
this 는 스프링 빈으로 등록되어 있는 프록시 객체를 대상으로 포인트컷을 매칭한다.
target 은 실제 target 객체를 대상으로 포인트컷을 매칭한다.
프록시 생성 방식에 따른 차이
스프링은 프록시를 생성할 때 JDK 동적 프록시와 CGLIB를 선택할 수 있다. 둘의 프록시를 생성하는 방식이 다르기 때문에 차이가 발생한다.
JDK 동적 프록시: 인터페이스가 필수이고, 인터페이스를 구현한 프록시 객체를 생성한다.
CGLIB: 인터페이스가 있어도 구체 클래스를 상속 받아서 프록시 객체를 생성한다.
먼저 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 적용 대상이다.
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 의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수 있다는 점을 알아두자.
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.
args
args: 인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭기본 문법은
execution의args부분과 같다.execution과 args의 차이점
execution은 파라미터 타입이 정확하게 매칭되어야 한다.execution은 클래스에 선언된 정보를 기반으로 판단한다.args는 부모 타입을 허용한다.args는 실제 넘어온 파라미터 객체 인스턴스를 보고 판단한다.:AspectJExpressionPointcut` 에 포인트컷은 한번만 지정할 수 있다. 이번 테스트에서는 테스트를 편리하게 진행하기 위해 포인트컷을 여러번 지정하기 위해 포인트컷 자체를 생성하는 메서드를 만들었다.String은Object,java.io.Serializable의 하위 타입이다.execution(* *(Object))는 매칭에 실패한다.args(Object)는 매칭에 성공한다. (부모 타입허용)참고
args지시자는 단독으로 사용되기 보다는 뒤에서 설명할 파라미터 바인딩에서 주로 사용된다.@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은 자기 자신의 클래스에 정의된 메서드에만 어드바이스를 적용한다.실행 결과
parentMethod()는Parent클래스에만 정의되어 있고,Child클래스에 정의되어 있지 않기 때문에@within에서 AOP 적용 대상이 되지 않는다.실행결과를 보면
child.parentMethod()를 호출 했을 때[@within]이 호출되지 않은 것을 확인할 수 있다.참고
주의
@annotation, @Args
@annotation
정의
@annotation: 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭설명
@annotation(hello.aop.member.annotation.MethodAop)다음과 같이 메서드(조인 포인트)에 애노테이션이 있으면 매칭한다.
실행 결과
@Args
정의
@args: 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트설명
전달된 인수의 런타임 타입에
@Check애노테이션이 있는 경우에 매칭한다.@args(test.Check)bean
정의
bean: 스프링 전용 포인트컷 지시자, 빈의 이름으로 지정한다.설명
bean(orderService) || bean(*Repository)*과 같은 패턴을 사용할 수 있다.OrderService,*Repository(OrderRepository)의 메서드에 AOP가 적용된다.실행 결과
매개변수 전달
다음은 포인트컷 표현식을 사용해서 어드바이스에 매개변수를 전달할 수 있다.
this, target, args,@target, @Within, @annotation, @Args
다음과 같이 사용한다.
arg로 맞추었다.String으로 되어 있기 때문에 다음과 같이 정의되는 것으로 이해하면 된다.args(arg,..)args(String,..):joinPoint.getArgs()[0]` 와 같이 매개변수를 전달 받는다.logArgs2:args(arg,..)와 같이 매개변수를 전달 받는다.logArgs3:@Before를 사용한 축약 버전이다. 추가로 타입을String으로 제한했다.this: 프록시 객체를 전달 받는다.target: 실제 대상 객체를 전달 받는다.@target,@within: 타입의 애노테이션을 전달 받는다.@annotation: 메서드의 애노테이션을 전달 받는다. 여기서는annotation.value()로 해당 애노테이션의 값을 출력하는 모습을 확인할 수 있다.실행 결과
순서는 조정했음
this, target
정의
this: 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트target: Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트설명
this,target은 다음과 같이 적용 타입 하나를 정확하게 지정해야 한다.*같은 패턴을 사용할 수 없다.this vs target
단순히 타입 하나를 정하면 되는데,
this와target은 어떤 차이가 있을까?스프링에서 AOP를 적용하면 실제
target객체 대신에 프록시 객체가 스프링 빈으로 등록된다.this는 스프링 빈으로 등록되어 있는 프록시 객체를 대상으로 포인트컷을 매칭한다.target은 실제 target 객체를 대상으로 포인트컷을 매칭한다.프록시 생성 방식에 따른 차이
스프링은 프록시를 생성할 때 JDK 동적 프록시와 CGLIB를 선택할 수 있다. 둘의 프록시를 생성하는 방식이 다르기 때문에 차이가 발생한다.
먼저 JDK 동적 프록시를 적용했을 때
this,target을 알아보자.MemberService 인터페이스 지정
this(hello.aop.member.MemberService)this는 부모 타입을 허용하기 때문에 AOP가 적용된다.target(hello.aop.member.MemberService)target은 부모 타입을 허용하기 때문에 AOP가 적용된다.MemberServiceImpl 구체 클래스 지정
this(hello.aop.member.MemberServiceImpl): proxy 객체를 보고 판단한다. JDK 동적 프록시로 만들어진 proxy 객체는MemberService인터페이스를 기반으로 구현된 새로운 클래스다. 따라서MemberServiceImpl를 전혀 알지 못하므로 AOP 적용 대상이 아니다.target(hello.aop.member.MemberServiceImpl): target 객체를 보고 판단한다. target 객체가MemberServiceImpl타입이므로 AOP 적용 대상이다.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의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수 있다는 점을 알아두자.this,target은 실제 객체를 만들어야 테스트 할 수 있다. 테스트에서 스프링 컨테이너를 사용해서target,proxy객체를 모두 만들어서 테스트해보자.properties = {"spring.aop.proxy-target-class=false"}:application.properties에 설정하는 대신에 해당 테스트에서만 설정을 임시로 적용한다. 이렇게 하면 각 테스트마다 다른 설정을 손쉽게 적용할 수 있다.spring.aop.proxy-target-class=false: 스프링이 AOP 프록시를 생성할 때 JDK 동적 프록시를 우선 생성한다. 물론 인터페이스가 없다면 CGLIB를 사용한다.spring.aop.proxy-target-class=true: 스프링이 AOP 프록시를 생성할 때 CGLIB 프록시를 생성한다. 참고로 이 설정을 생략하면 스프링 부트에서 기본으로 CGLIB를 사용한다. 이 부분은 뒤에서 자세히 설명한다.spring.aop.proxy-target-class=false
JDK 동적 프록시 사용
JDK 동적 프록시를 사용하면
this(hello.aop.member.MemberServiceImpl)로 지정한[this-impl]부분이 출력되지 않는 것을 확인할 수 있다.spring.aop.proxy-target-class=true, 또는 생략(스프링 부트 기본 옵션)
CGLIB 사용
참고
All reactions