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
new DefaultPointcutAdvisor : Advisor 인터페이스의 가장 일반적인 구현체이다. 생성자를 통해 하나 의 포인트컷과 하나의 어드바이스를 넣어주면 된다. 어드바이저는 하나의 포인트컷과 하나의 어드바이스로 구성된다.
Pointcut.TRUE : 항상 true 를 반환하는 포인트컷이다. 이후에 직접 포인트컷을 구현해볼 것이다.
new TimeAdvice() : 앞서 개발한 TimeAdvice 어드바이스를 제공한다.
proxyFactory.addAdvisor(advisor) : 프록시 팩토리에 적용할 어드바이저를 지정한다. 어드바이저는 내부에 포인트컷과 어드바이스를 모두 가지고 있다. 따라서 어디에 어떤 부가 기능을 적용해야 할지 어드바이저 하나로 알 수 있다. 프록시 팩토리를 사용할 때 어드바이저는 필수이다.
그런데 생각해보면 이전에 분명히 proxyFactory.addAdvice(new TimeAdvice()) 이렇게 어드바이저가 아니라 어드바이스를 바로 사용했다.
@Test@DisplayName("인터페이스가 있으면 JDK 동적 프록시 사용")
voidinterfaceProxy() {
ServiceInterfacetarget = newServiceImpl();
ProxyFactoryproxyFactory = newProxyFactory(target);
proxyFactory.addAdvice(newTimeAdvice());
...
코드를 따라가다 보면
@OverridepublicvoidaddAdvice(intpos, Adviceadvice) throwsAopConfigException {
Assert.notNull(advice, "Advice must not be null");
if (adviceinstanceofIntroductionInfo) {
// We don't need an IntroductionAdvisor for this kind of introduction:// It's fully self-describing.addAdvisor(pos, newDefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
}
elseif (adviceinstanceofDynamicIntroductionAdvice) {
// We need an IntroductionAdvisor for this kind of introduction.thrownewAopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
}
else {
addAdvisor(pos, newDefaultPointcutAdvisor(advice));
}
}
Pointcut.TRUE 가 들어간다.
이것은 단순히 편의 메서드이고 결과적으로 해당 메서드 내부에서 지금 코드와 똑같은 다음 어드바이저가 생성된다. DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice())
프록시 팩토리가 어드바이저를 알고있고, 어드바이저는 Pointcut 과 Advice를 알고 있다.
프록시의 save() 를 호출하면, 먼저 Pointcut 에 물어본다.
save() 에 프록시를 적용해도 되는지? 근데 지금은 무조건 True 를 반환하니 OK.
그럼 Advice 가 적용되어 save 를 호출함.
만약 Pointcut 이 False 를 반환하면, 프록시에서 대상 메서드를 바로 호출한다.
예제 코드2 - 직접 만든 포인트컷
save() 메서드엔 어드바이스 로직 적용하지만,
find() 메서드에는 어드바이스 로직 적용하지 않도록 해보자.
이전처럼 어드바이스 로직에서 메서드 이름을 보고 실행할지 말지 분시를 타고 되지만,
이렇게되면 해당 코드의 재사용성이 떨어지도, 뭔가 역할과 구현이 구분되지 않음.
포인트컷은 크게 ClassFilter 와 MethodMatcher 둘로 이루어진다. 이름 그대로 하나는 클래스가 맞는지, 하나는 메서드가 맞는지 확인할 때 사용한다. 둘다 true로 반환해야 어드바이스를 적용할 수 있다.
클래스 필터의 경우 굳이 사용하지 않고 메서드 이름만 맞춰도 될것 같은데? 그래서 보통은 ClassFilter 에는 true 를 사용한다. 자세한 사용은 다음에
MyPointcut
직접 구현한 포인트컷이다. Pointcut 인터페이스를 구현한다.
현재 메서드 기준으로 로직을 적용하면 된다. 클래스 필터는 항상 true 를 반환하도록 했고, 메서드 비교 기능은 MyMethodMatcher 를 사용한다.
MyMethodMatcher
직접 구현한 MethodMatcher 이다. MethodMatcher 인터페이스를 구현한다.
matches() : 이 메서드에 method , targetClass 정보가 넘어온다. 이 정보로 어드바이스를 적용할지 적용 하지 않을지 판단할 수 있다. 여기서는 메서드 이름이 "save" 인 경우에 true 를 반환하도록 판단 로직을 적용했다. isRuntime() , matches(... args) : isRuntime() 이 값이 참이면 matches(... args) 메서드가 대신 호출된다. 동적으로 넘어오는 매개변수를 판단 로직으로 사용할 수 있다.
isRuntime() 이 False 면 matches 메서드가 호출되는데, 이 메서드에서 사용하는 파라미터는 정적이기 때문에 캐싱해서 사용 가능.
isRuntime() 이 true 인 경우 매개변수가 동적으로 변경된다고 가정하기 때문에 캐싱을 하지 않는다.
new DefaultPointcutAdvisor(new MyPointcut(), new TimeAdvice())
어드바이저에 직접 구현한 포인트컷을 사용한다.
실행 결과
클라이언트가 프록시의 save() 호출
포인트컷에 Service 클래스의 save() 메서드에 어드바이스를 적용해도 될지 물어본다.
포인트컷이 true 를 반환한다. 따라서 어드바이스를 호출해서 부가 기능을 적용한다.
이후 실제 인스턴스의 save() 를 호출한다.
클라이언트가 프록시의 find() 를 호출한다.
포인트컷에 Service 클래스의 find() 메서드에 어드바이스를 적용해도 될지 물어본다.
포인트컷이 false 를 반환한다. 따라서 어드바이스를 호출하지 않고, 부가 기능도 적용되지 않는다.
실제 인스턴스를 호출한다.
예제 코드3 - 스프링이 제공하는 포인트컷
스프링은 우리가 필요한 포인트컷을 이미 대부분 제공한다.
이번에는 스프링이 제공하는 NameMatchMethodPointcut 를 사용해서 구현해보자.
스프링이 제공하는 포인트컷
스프링은 무수히 많은 포인트컷을 제공한다.
대표적인 몇가지만 알아보자.
NameMatchMethodPointcut : 메서드 이름을 기반으로 매칭한다. 내부에서는 PatternMatchUtils 를 사용한다.
예) *xxx* 허용
JdkRegexpMethodPointcut : JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.
TruePointcut : 항상 참을 반환한다.
AnnotationMatchingPointcut : 애노테이션으로 매칭한다.
AspectJExpressionPointcut : aspectJ 표현식으로 매칭한다.
가장 중요한 것은 aspectJ 표현식
여기에서 사실 다른 것은 중요하지 않다. 실무에서는 사용하기도 편리하고 기능도 가장 많은 aspectJ 표현식을 기반으로 사용하는 AspectJExpressionPointcut 을 사용하게 된다.
aspectJ 표현식과 사용방법은 중요해서 이후 AOP를 설명할 때 자세히 설명하겠다.
지금은 Pointcut 의 동작 방식과 전체 구조에 집중하자.
예제 코드4 - 여러 어드바이저 함께 적용
어드바이저는 하나의 포인트컷과 하나의 어드바이스를 가지고 있다.
만약 여러 어드바이저를 하나의 target 에 적용하려면 어떻게 해야할까?
쉽게 이야기해서 하나의 target 에 여러 어드바이스를 적용하려면 어떻게 해야할까?
클라이언트가 동적 프록시를 호출해서 advisor2 를 호출하고, 그다음에 또 동적 프록시를 출해서 advisor1 을 호출하고 마지막에 타겟 메서드 호출하도록
프록시 팩토리에 원하는 만큼 addAdvisor() 를 통해서 어드바이저를 등록하면 된다.
등록하는 순서대로 advisor 가 호출된다. 여기서는 advisor2 , advisor1 순서로 등록했다.
- 클라이언트가 프록시 호출, 프록시는 advisor2 호출
- advisor2 를 만들때 넣어준 pointcut 으로 확인 (지금은 항상 true)
- 이후 advisor1 호출하고 마찬가지로 pointcut 확인
- 마지막으로 타겟 메서드 호출
정리
결과적으로 여러 프록시를 사용할 때와 비교해서 결과는 같고, 성능은 더 좋다.
중요
사실 이번 장을 이렇게 풀어서 설명한 이유가 있다. 스프링의 AOP를 처음 공부하거나 사용하면, AOP 적용 수 만큼 프록시가 생성된다고 착각하게 된다.
스프링은 AOP를 적용할 때, 최적화를 진행해서 지금처럼 프록시는 하나만 만들고, 하나의 프록시에 여러 어드바이저를 적용한다.
정리하면 하나의 target 에 여러 AOP가 동시에 적용되어도, 스프링의 AOP는 target 마다 하나의 프록시만생성한다. 이부분을 꼭 기억해두자.
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.
-
포인트컷, 어드바이스, 어드바이저 - 소개
스프링 AOP 를 공부했다면 들어봤을 단어들.
중요한 단어들이니 확실히 정리해보자.
Pointcut: 어디에 부가 기능을 적용할지? 어디에 부가 기능을 적용하지 않을지 판단하는 필터링 로직.주로 클래스와 메서드 이름으로 필터링 한다. 이름 그대로 어떤 포인트(point)에 기능을 적용하지, 하지 않을지 잘라서(Cut) 구분하는 것
Advice: 프폴기사 호출하는 부가 기능. 단순하게 프록시 로직.Advisor: 단순하게 하나의 포인트컷과 하나의 어드바이스를 가지고 있는것. (포인트컷1 + 어드바이스1)정리하면 부가기능 로직을 적용해야 하는데
어드바이져쉽게 기억하기
Advice)을 어디(Pointcut)에 할 것인가?Advisor)는 어디(Pointcut)에 조언(Advice)을 해야할지 알고 있다.역할과 책임
이렇게 구분한 것은 역할과 책임을 명확하게 분리한 것이다.
이전에 만들었던걸 한번 살펴보자.
(JDK 동적 프록시를 만들었던 코드)
여기서
위와 같이 적용 필요한 필터링을 메서드 이름 기준으로 직접 넣어줬다.
즉 로직도 있고 필터링도 있어서 두가지 기능이 한번에 들어가 있음.
참고
해당 단어들에 대한 정의는 지금은 문맥상 이해를 돕기 위해 프록시에 맞추어서 설명하지만, 이후에 AOP 부분에 서 다시 한번 AOP에 맞추어 정리하겠다. 그림은 이해를 돕기 위한 것이고, 실제 구현은 약간 다를 수 있다.
예제 코드1 - 어드바이저
어드바이저는 하나의 포인트컷과 하나의 어드바이스를 가지고 있다.
프록시 팩토리를 통해 프록시를 생성할 때 어드바이저를 제공하면 어디에 어떤 기능을 제공할 지 알 수 있다.
테스트코드로 확인해보자.
실행 결과

new DefaultPointcutAdvisor:Advisor인터페이스의 가장 일반적인 구현체이다. 생성자를 통해 하나 의 포인트컷과 하나의 어드바이스를 넣어주면 된다. 어드바이저는 하나의 포인트컷과 하나의 어드바이스로 구성된다.Pointcut.TRUE: 항상true를 반환하는 포인트컷이다. 이후에 직접 포인트컷을 구현해볼 것이다.new TimeAdvice(): 앞서 개발한TimeAdvice어드바이스를 제공한다.proxyFactory.addAdvisor(advisor): 프록시 팩토리에 적용할 어드바이저를 지정한다. 어드바이저는 내부에 포인트컷과 어드바이스를 모두 가지고 있다. 따라서 어디에 어떤 부가 기능을 적용해야 할지 어드바이저 하나로 알 수 있다. 프록시 팩토리를 사용할 때 어드바이저는 필수이다.proxyFactory.addAdvice(new TimeAdvice())이렇게 어드바이저가 아니라 어드바이스를 바로 사용했다.코드를 따라가다 보면
DefaultPointcutAdvisor가 들어가게 되고 이것은Pointcut.TRUE가 들어간다.이것은 단순히 편의 메서드이고 결과적으로 해당 메서드 내부에서 지금 코드와 똑같은 다음 어드바이저가 생성된다.
DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice())프록시 팩토리가 어드바이저를 알고있고, 어드바이저는 Pointcut 과 Advice를 알고 있다.
프록시의 save() 를 호출하면, 먼저 Pointcut 에 물어본다.
save() 에 프록시를 적용해도 되는지? 근데 지금은 무조건 True 를 반환하니 OK.
그럼 Advice 가 적용되어 save 를 호출함.
만약 Pointcut 이 False 를 반환하면, 프록시에서 대상 메서드를 바로 호출한다.
예제 코드2 - 직접 만든 포인트컷
save() 메서드엔 어드바이스 로직 적용하지만,
find() 메서드에는 어드바이스 로직 적용하지 않도록 해보자.
이전처럼 어드바이스 로직에서 메서드 이름을 보고 실행할지 말지 분시를 타고 되지만,
이렇게되면 해당 코드의 재사용성이 떨어지도, 뭔가 역할과 구현이 구분되지 않음.
이런 기능에 특화되어서 제공되는 것이 바로 포인트컷이다.
포인트컷은 크게
ClassFilter와MethodMatcher둘로 이루어진다. 이름 그대로 하나는 클래스가 맞는지, 하나는 메서드가 맞는지 확인할 때 사용한다. 둘다true로 반환해야 어드바이스를 적용할 수 있다.클래스 필터의 경우 굳이 사용하지 않고 메서드 이름만 맞춰도 될것 같은데? 그래서 보통은 ClassFilter 에는 true 를 사용한다. 자세한 사용은 다음에
MyPointcut
직접 구현한 포인트컷이다.
Pointcut인터페이스를 구현한다.현재 메서드 기준으로 로직을 적용하면 된다. 클래스 필터는 항상
true를 반환하도록 했고, 메서드 비교 기능은MyMethodMatcher를 사용한다.MyMethodMatcher
MethodMatcher이다.MethodMatcher인터페이스를 구현한다.matches(): 이 메서드에method,targetClass정보가 넘어온다. 이 정보로 어드바이스를 적용할지 적용 하지 않을지 판단할 수 있다. 여기서는 메서드 이름이"save"인 경우에true를 반환하도록 판단 로직을 적용했다.isRuntime(),matches(... args):isRuntime()이 값이 참이면matches(... args)메서드가 대신 호출된다. 동적으로 넘어오는 매개변수를 판단 로직으로 사용할 수 있다.isRuntime()이 False 면matches메서드가 호출되는데, 이 메서드에서 사용하는 파라미터는 정적이기 때문에 캐싱해서 사용 가능.isRuntime()이true인 경우 매개변수가 동적으로 변경된다고 가정하기 때문에 캐싱을 하지 않는다.new DefaultPointcutAdvisor(new MyPointcut(), new TimeAdvice())
어드바이저에 직접 구현한 포인트컷을 사용한다.
실행 결과

save()호출Service클래스의save()메서드에 어드바이스를 적용해도 될지 물어본다.true를 반환한다. 따라서 어드바이스를 호출해서 부가 기능을 적용한다.save()를 호출한다.find()를 호출한다.Service클래스의find()메서드에 어드바이스를 적용해도 될지 물어본다.false를 반환한다. 따라서 어드바이스를 호출하지 않고, 부가 기능도 적용되지 않는다.예제 코드3 - 스프링이 제공하는 포인트컷
스프링은 우리가 필요한 포인트컷을 이미 대부분 제공한다.
이번에는 스프링이 제공하는
NameMatchMethodPointcut를 사용해서 구현해보자.스프링이 제공하는 포인트컷
스프링은 무수히 많은 포인트컷을 제공한다.
대표적인 몇가지만 알아보자.
NameMatchMethodPointcut: 메서드 이름을 기반으로 매칭한다. 내부에서는PatternMatchUtils를 사용한다.*xxx*허용JdkRegexpMethodPointcut: JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.TruePointcut: 항상 참을 반환한다.AnnotationMatchingPointcut: 애노테이션으로 매칭한다.AspectJExpressionPointcut: aspectJ 표현식으로 매칭한다.가장 중요한 것은 aspectJ 표현식
여기에서 사실 다른 것은 중요하지 않다. 실무에서는 사용하기도 편리하고 기능도 가장 많은 aspectJ 표현식을 기반으로 사용하는
AspectJExpressionPointcut을 사용하게 된다.aspectJ 표현식과 사용방법은 중요해서 이후 AOP를 설명할 때 자세히 설명하겠다.
지금은
Pointcut의 동작 방식과 전체 구조에 집중하자.예제 코드4 - 여러 어드바이저 함께 적용
어드바이저는 하나의 포인트컷과 하나의 어드바이스를 가지고 있다.
만약 여러 어드바이저를 하나의
target에 적용하려면 어떻게 해야할까?쉽게 이야기해서 하나의
target에 여러 어드바이스를 적용하려면 어떻게 해야할까?클라이언트가 동적 프록시를 호출해서 advisor2 를 호출하고, 그다음에 또 동적 프록시를 출해서 advisor1 을 호출하고 마지막에 타겟 메서드 호출하도록
실행 결과

런타임에 동작

여러 프록시의 문제
이 방법이 잘못된 것은 아니지만, 프록시를 2번 생성해야 한다는 문제가 있다. 만약 적용해야 하는 어드바이저가 10개라면 10개의 프록시를 생성해야한다.
스프링은 이 문제를 해결하기 위해 하나의 프록시에 여러 어드바이저를 적용할 수 있게 만들어두었다.

addAdvisor()를 통해서 어드바이저를 등록하면 된다.advisor가 호출된다. 여기서는advisor2,advisor1순서로 등록했다.정리
결과적으로 여러 프록시를 사용할 때와 비교해서 결과는 같고, 성능은 더 좋다.
중요
사실 이번 장을 이렇게 풀어서 설명한 이유가 있다. 스프링의 AOP를 처음 공부하거나 사용하면, AOP 적용 수 만큼 프록시가 생성된다고 착각하게 된다.
스프링은 AOP를 적용할 때, 최적화를 진행해서 지금처럼 프록시는 하나만 만들고, 하나의 프록시에 여러 어드바이저를 적용한다.
정리하면 하나의
target에 여러 AOP가 동시에 적용되어도, 스프링의 AOP는target마다 하나의 프록시만생성한다. 이부분을 꼭 기억해두자.Beta Was this translation helpful? Give feedback.
All reactions