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
빈 후처리기를 사용해서 실제 객체 대신 프록시를 스프링 빈으로 등록해보자.
이렇게 하면 수동으로 등록하는 빈은 물론이고, 컴포넌트 스캔을 사용하는 빈까지 모두 프록시를 적용할 수 있다.
더 나아가서 설정 파일에 있는 수 많은 프록시 생성 코드도 한번에 제거할 수 있다.
지금까지 빈(프록시) 등록 한걸 자동으로 해준다?
@Slf4jpublicclassPackageLogTracePostProcessorimplementsBeanPostProcessor {
// 특정 패키지 하위만 적용privatefinalStringbasePackage;
privatefinalAdvisoradvisor;
publicPackageLogTracePostProcessor(StringbasePackage, Advisoradvisor) {
this.basePackage = basePackage;
this.advisor = advisor;
}
@OverridepublicObjectpostProcessAfterInitialization(Objectbean, StringbeanName) throwsBeansException {
// 빈 객체 초기화 (빈 객체가 다 만들어진 다음에) 프록시 적용log.info("param beanName={} bean={}", beanName, bean.getClass());
// 프록시 적용 대상 여부 체크// 프록시 적용 대상이 아니면 원본을 그대로 진행StringpackageName = bean.getClass().getPackageName();
if (!packageName.startsWith(basePackage)) {
returnbean;
}
// 프록시 대상이면 프록시를 만들어서 반환ProxyFactoryproxyFactory = newProxyFactory(bean);
proxyFactory.addAdvisor(advisor);
Objectproxy = proxyFactory.getProxy();
log.info("create proxy: target={} proxy={}", bean.getClass(), proxy.getClass());
returnproxy;
}
}
PackageLogTraceProxyPostProcessor 는 원본 객체를 프록시 객체로 변환하는 역할을 한다. 이때 프록시 팩토리를 사용하는데, 프록시 팩토리는 advisor 가 필요하기 때문에 이 부분은 외부에서 주입 받도록 했다.
스프링 자체 빈이 매우 많기 때문에 패키지 제한을 해줘야함!
프록시 적용 대상의 반환 값을 보면 원본 객체 대신에 프록시 객체를 반환한다. 따라서 스프링 컨테이너에 원본 객체 대신에 프록시 객체가 스프링 빈으로 등록된다. 원본 객체는 스프링 빈으로 등록되지 않는다.
만든 beanprocessor 등록하기
@Slf4j@Configuration@Import({AppV1Config.class, AppV2Config.class}) // v1, v2 는 컴포넌트 스캔 대상이 아니기 때문에 빈등록 필요publicclassBeanPostProcessorConfig {
@BeanpublicPackageLogTracePostProcessorlogTracePostProcessor(LogTracelogTrace) {
returnnewPackageLogTracePostProcessor("hello.proxy.app", getAdvisor(logTrace));
}
privateAdvisorgetAdvisor(LogTracelogTrace) {
//pointcutNameMatchMethodPointcutpointcut = newNameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
//adviceLogTraceAdviceadvice = newLogTraceAdvice(logTrace);
returnnewDefaultPointcutAdvisor(pointcut, advice);
}
}
@Import({AppV1Config.class, AppV2Config.class}) : V3는 컴포넌트 스캔으로 자동으로 스프링빈으로 등록되지만, V1, V2 애플리케이션은 수동으로 스프링 빈으로 등록해야 동작한다.ProxyApplication 에서 등록해도 되지만 편의상 여기에 등록하자.
@Bean logTraceProxyPostProcessor() : 특정 패키지를 기준으로 프록시를 생성하는 빈 후처리기를 스프링 빈으로 등록한다. 빈 후처리기는 스프링 빈으로만 등록하면 자동으로 동작한다. 여기에 프록시를 적용할 패키지 정보(hello.proxy.app )와 어드바이저(getAdvisor(logTrace) )를 넘겨준다.
이제 프록시를 생성하는 코드가 설정 파일에는 필요 없다. 순수한 빈 등록만 고민하면 된다. 프록시를 생성하고 프록시를 스프링 빈으로 등록하는 것은 빈 후처리기가 모두 처리해준다.
스프링 빈 중에 final 빈도 있고 이것은 프록시 만들지 못하기 때문에 패키기 제한 하지 않으면 에러 발생할 수 있다.
컴포넌트 스캔에도 적용
여기서 중요한 포인트는 v1, v2와 같이 수동으로 등록한 빈 뿐만 아니라 컴포넌트 스캔을 통해 등록한 v3 빈들도 프록시를 적용할 수 있다는 점이다. 이것은 모두 빈 후처리기 덕분이다.
프록시 적용 대상 여부 체크
애플리케이션을 실행해서 로그를 확인해보면 알겠지만, 우리가 직접 등록한 스프링 빈들 뿐만 아니라 스프링 부트가 기본으로 등록하는 수 많은 빈들이 빈 후처리기에 넘어온다. 그래서 어떤 빈을 프록시로 만들 것인지 기준이 필요하다. 여기서는 간단히 basePackage 를 사용해서 특정 패키지를 기준으로 해당 패키지와 그 하위 패키지의 빈들을 프록시로 만든다. -> 나중엔 포인트컷을 활용해 더 간단하게 ? 할 수 있다.
스프링 부트가 기본으로 제공하는 빈 중에는 프록시 객체를 만들 수 없는 빈들도 있다. 따라서 모든 객체를 프록시로 만들 경우 오류가 발생한다.
빈 후처리기 - 정리
이전에 보았던 문제들이 빈 후처리기를 통해서 어떻게 해결되었는지 정리해보자.
문제 1 - 너무 많은 설정
프록시를 직접 스프링 빈으로 등록하는 ProxyFactoryConfigV1 , ProxyFactoryConfigV2 와 같은 설정 파일은 프록시 관련 설정이 지나치게 많다는 문제가 있다.
스프링 빈이 100개 있으면 100개 프록시 설정 코드가 들어가야 한다.
빈을 편리하게 등록하려고 컴포넌트 스캔을 사용했는데, 프록시를 따로 등록해야 했고 심지어 프록시 생성 코드까지 필요했음.
문제 2 - 컴포넌트 스캔
애플리케이션 V3처럼 컴포넌트 스캔을 사용하는 경우 지금까지 학습한 방법으로는 프록시 적용이 불가능했다.
왜냐하면 컴포넌트 스캔으로 이미 스프링 컨테이너에 실제 객체를 스프링 빈으로 등록을 다 해버린 상태이기 때문이다.
우리가 전까지 학습한 코드는 실제 객체를 스프링 빈으로 등록하는게 아니라 프록시를 등록해야 했는데,
이미 컴포넌트 스캔을 통해 원본 객체가 스프링 빈으로 자동 등록되어 프록시 적용이 불가능했다.
문제 해결
빈 후처리기 덕분에 프록시를 생성하는 부분을 하나로 집중할 수 있다. 그리고 컴포넌트 스캔처럼 스프링이 직접 대상을 빈으로 등록하는 경우에도 중간에 빈 등록 과정을 가로채서 원본 대신에 프록시를 스프링 빈으로 등록할 수 있다.
덕분에 애플리케이션에 수 많은 스프링 빈이 추가되어도 프록시와 관련된 코드는 전혀 변경하지 않아도 된다. 그리고 컴포넌트 스캔을 사용해도 프록시가 모두 적용된다.
하지만 개발자의 욕심은 끝이 없다.
스프링은 프록시를 생성하기 위한 빈 후처리기를 이미 만들어서 제공한다. (스프링이 제공하는 빈 후처리기를 가져다가 쓰면 된다!)
중요
프록시의 적용 대상 여부를 여기서는 간단히 패키지를 기준으로 설정했다. 그런데 잘 생각해보면 포인트컷을 사용하면 더 깔끔할 것 같다.
포인트컷은 이미 클래스, 메서드 단위의 필터 기능을 가지고 있기 때문에, 프록시 적용 대상 여부를 정밀하게 설정 할 수 있다.
참고로 어드바이저는 포인트컷을 가지고 있다. 따라서 어드바이저를 통해 포인트컷을 확인할 수 있다.
뒤에서 학습하겠지만 스프링 AOP는 포인트컷을 사용해서 프록시 적용 대상 여부를 체크한다.
결과적으로 포인트컷은 다음 두 곳에 사용된다.
프록시 적용 대상 여부를 체크해서 꼭 필요한 곳에만 프록시를 적용한다. (빈 후처리기 - 자동 프록시 생성)
이 라이브러리를 추가하면 aspectjweaver 라는 aspectJ 관련 라이브러리를 등록하고, 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다. 스프링 부트가 없던 시절에는 @EnableAspectJAutoProxy 를 직접 사용 해야 했는데, 이 부분을 스프링 부트가 자동으로 처리해준다. aspectJ 는 뒤에서 설명한다. 스프링 부트가 활성화하는 빈은 AopAutoConfiguration 를 참고하자. (스프링 부트가 이걸 자동으로 켜줌)
결국 뭐가 최종적으로 되느냐?
자동 프록시 생성기가 활성화 된다 (스프링빈에 등록된다)
자동 프록시 생성기 - AutoProxyCreator
앞서 이야기한 스프링 부트 자동 설정으로 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기가 스프링 빈에 자동으로 등록된다.
이름 그대로 자동으로 프록시를 생성해주는 빈 후처리기이다.
이 빈 후처리기는 스프링 빈으로 등록된 Advisor 들을 자동으로 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다.
Advisor 안에는 Pointcut 과 Advice 가 이미 모두 포함되어 있다. 따라서 Advisor 만 알고 있으면 그 안에 있는 Pointcut으로 어떤 스프링 빈에 프록시를 적용해야 할지 알 수 있다. 그리고 Advice로 부가 기능을 적용하면 된다. 그래서 Advisor 만 찾으면 된다!
참고
AnnotationAwareAspectJAutoProxyCreator 는 @AspectJ와 관련된 AOP 기능도 자동으로 찾아서 처리해준다.
Advisor는 물론이고,@aspect도 자동으로 인식해서 프록시를 만들고 AOP를 적용해준다.@Aspect` 에 대한 자세한 내용은 뒤에 설명한다.
AnnotationAwareAspectJAutoProxyCreator 의 아주 위쪽 부모에 BeanPostProcessor 가 있음.
1. 생성: 스프링이 스프링 빈 대상이 되는 객체를 생성한다. (@Bean , 컴포넌트 스캔 모두 포함) 2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다. 3. 모든 Advisor 빈 조회: 자동 프록시 생성기 - 빈 후처리기는 스프링 컨테이너에서 모든 Advisor 를 조회한다. 4. 프록시 적용 대상 체크: 앞서 조회한 Advisor 에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다. 이때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭해본다. 그래서 조건이 하나라도 만족하면 프록시 적용 대상이 된다. 예를 들어서 10개의 메서드 중에 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 된다. 5. 프록시 생성: 프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록한다. 만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록한다. 6. 빈 등록: 반환된 객체는 스프링 빈으로 등록된다.
포인트컷을 두개를 구분해야하는데
Proxy 를 만드는 단계에서 쓰이는 포인트컷과 (프록시를 만들지 말지)
실제 실행 단계에서 쓰이는 포인트컷 (메서드에 프록시 코드 (어드바이저 코드) 를 적용할지 말지)
자동 프록시 생성기는 포인트컷을 사용해서 해당 빈이 프록시를 생성할 필요가 있는지 없는지 체크한다.
클래스 + 메서드 조건을 모두 비교한다. 이때 모든 메서드를 체크하는데, 포인트컷 조건에 하나하나 매칭해 본다. 만약 조건에 맞는 것이 하나라도 있으면 프록시를 생성한다.
예) orderControllerV1 은 request() , noLog() 가 있다. 여기에서 request() 가 조건에 만족하므로 프록시를 생성한다.
만약 조건에 맞는 것이 하나도 없으면 프록시를 생성할 필요가 없으므로 프록시를 생성하지 않는다.
2. 어드바이스 적용 여부 판단 - 사용 단계
프록시가 호출되었을 때 (클라이언트에서 요청이 왔을때!) 부가 기능인 어드바이스를 적용할지 말지 포인트컷을 보고 판단한다.
앞서 설명한 예에서 orderControllerV1 은 이미 프록시가 걸려있다.
orderControllerV1 의 request() 는 현재 포인트컷 조건에 만족하므로 프록시는 어드바이스를 먼저 호출하고, target 을 호출한다.
orderControllerV1 의 noLog() 는 현재 포인트컷 조건에 만족하지 않으므로 어드바이스를 호출하지 않고 바로 target 만 호출한다.
참고: 프록시를 모든 곳에 생성하는 것은 비용 낭비이다. 꼭 필요한 곳에 최소한의 프록시를 적용해야 한다. 그래서 자동 프록시 생성기는 모든 스프링 빈에 프록시를 적용하는 것이 아니라 포인트컷으로 한번 필터링해서 어드바이스가 사용될 가능성이 있는 곳에만 프록시를 생성한다.
애플리케이션 서버를 실행해보면, 스프링이 초기화 되면서 기대하지 않은 이러한 로그들이 올라온다. 그 이유는 지금 사용한 포인트컷이 단순히 메서드 이름에 "request*", "order*", "save*" 만 포함되어 있으면 매칭 된다고 판단하기 때문이다.
결국 스프링이 내부에서 사용하는 빈에도 메서드 이름에 request 라는 단어만 들어가 있으면 프록시가 만들어지고 되고, 어드바이스도 적용되는 것이다.
결론적으로 패키지에 메서드 이름까지 함께 지정할 수 있는 매우 정밀한 포인트컷이 필요하다.
AspectJExpressionPointcut
AspectJ라는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다. AspectJ 포인트컷 표현식과 AOP는 조금 뒤에 자세히 설명하겠다. 지금은 특별한 표현식으로 복잡한 포인트컷을 만들 수 있구나 라고 대략 이해하면 된다.
hello.proxy.app 패키지와 하위 패키지의 모든 메서드는 포인트컷에 매칭하되, noLog() 메서드는 제외하라는 뜻이다.
이제 no-log 요청시 로그가 남지 않는다.
하나의 프록시, 여러 Advisor 적용
예를 들어서 어떤 스프링 빈이 advisor1 , advisor2 가 제공하는 포인트컷의 조건을 모두 만족하면 프록시 자동 생성기는 프록시를 몇 개 생성할까?
프록시 자동 생성기는 프록시를 하나만 생성한다. 왜냐하면 프록시 팩토리가 생성하는 프록시는 내부에 여러 advisor 들을 포함할 수 있기 때문이다. 따라서 프록시를 여러 개 생성해서 비용을 낭비할 이유가 없다.
프록시 생성 상황별 정리
advisor1의 포인트컷만 만족 : 프록시 1개 생성, 프록시에 advisor1 만 포함
advisor1, advisor2 의 포인트컷 모두 만족 : 프록시 1개 생성, 프록시에 advisor1, advisor2 모두 포함
advisor1, advisor2 의 포인트컷 모두 만족 X : 프록시 생성되지 않음. 이후에 설명할 스프링 AOP도 동일한 방식으로 동작한다. (프록시는 여러개 생성되지 않는다!)
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.
빈 후처리기 - 적용
빈 후처리기를 사용해서 실제 객체 대신 프록시를 스프링 빈으로 등록해보자.
이렇게 하면 수동으로 등록하는 빈은 물론이고, 컴포넌트 스캔을 사용하는 빈까지 모두 프록시를 적용할 수 있다.
더 나아가서 설정 파일에 있는 수 많은 프록시 생성 코드도 한번에 제거할 수 있다.
지금까지 빈(프록시) 등록 한걸 자동으로 해준다?
PackageLogTraceProxyPostProcessor는 원본 객체를 프록시 객체로 변환하는 역할을 한다. 이때 프록시 팩토리를 사용하는데, 프록시 팩토리는advisor가 필요하기 때문에 이 부분은 외부에서 주입 받도록 했다.만든 beanprocessor 등록하기
@Import({AppV1Config.class, AppV2Config.class}): V3는 컴포넌트 스캔으로 자동으로 스프링빈으로 등록되지만, V1, V2 애플리케이션은 수동으로 스프링 빈으로 등록해야 동작한다.ProxyApplication에서 등록해도 되지만 편의상 여기에 등록하자.@Bean logTraceProxyPostProcessor(): 특정 패키지를 기준으로 프록시를 생성하는 빈 후처리기를 스프링 빈으로 등록한다. 빈 후처리기는 스프링 빈으로만 등록하면 자동으로 동작한다. 여기에 프록시를 적용할 패키지 정보(hello.proxy.app)와 어드바이저(getAdvisor(logTrace))를 넘겨준다.사라진 코드
참고 v1, v2 Config
실행시
v3 에서 프록시가 잘 적용되는것 확인 가능.
로그 요약
컴포넌트 스캔에도 적용
여기서 중요한 포인트는 v1, v2와 같이 수동으로 등록한 빈 뿐만 아니라 컴포넌트 스캔을 통해 등록한 v3 빈들도 프록시를 적용할 수 있다는 점이다. 이것은 모두 빈 후처리기 덕분이다.
프록시 적용 대상 여부 체크
basePackage를 사용해서 특정 패키지를 기준으로 해당 패키지와 그 하위 패키지의 빈들을 프록시로 만든다. -> 나중엔 포인트컷을 활용해 더 간단하게 ? 할 수 있다.빈 후처리기 - 정리
이전에 보았던 문제들이 빈 후처리기를 통해서 어떻게 해결되었는지 정리해보자.
문제 1 - 너무 많은 설정
프록시를 직접 스프링 빈으로 등록하는
ProxyFactoryConfigV1,ProxyFactoryConfigV2와 같은 설정 파일은 프록시 관련 설정이 지나치게 많다는 문제가 있다.스프링 빈이 100개 있으면 100개 프록시 설정 코드가 들어가야 한다.
빈을 편리하게 등록하려고 컴포넌트 스캔을 사용했는데, 프록시를 따로 등록해야 했고 심지어 프록시 생성 코드까지 필요했음.
문제 2 - 컴포넌트 스캔
애플리케이션 V3처럼 컴포넌트 스캔을 사용하는 경우 지금까지 학습한 방법으로는 프록시 적용이 불가능했다.
왜냐하면 컴포넌트 스캔으로 이미 스프링 컨테이너에 실제 객체를 스프링 빈으로 등록을 다 해버린 상태이기 때문이다.
우리가 전까지 학습한 코드는 실제 객체를 스프링 빈으로 등록하는게 아니라 프록시를 등록해야 했는데,
이미 컴포넌트 스캔을 통해 원본 객체가 스프링 빈으로 자동 등록되어 프록시 적용이 불가능했다.
문제 해결
빈 후처리기 덕분에 프록시를 생성하는 부분을 하나로 집중할 수 있다. 그리고 컴포넌트 스캔처럼 스프링이 직접 대상을 빈으로 등록하는 경우에도 중간에 빈 등록 과정을 가로채서 원본 대신에 프록시를 스프링 빈으로 등록할 수 있다.
덕분에 애플리케이션에 수 많은 스프링 빈이 추가되어도 프록시와 관련된 코드는 전혀 변경하지 않아도 된다. 그리고 컴포넌트 스캔을 사용해도 프록시가 모두 적용된다.
하지만 개발자의 욕심은 끝이 없다.
스프링은 프록시를 생성하기 위한 빈 후처리기를 이미 만들어서 제공한다. (스프링이 제공하는 빈 후처리기를 가져다가 쓰면 된다!)
중요
프록시의 적용 대상 여부를 여기서는 간단히 패키지를 기준으로 설정했다. 그런데 잘 생각해보면 포인트컷을 사용하면 더 깔끔할 것 같다.
포인트컷은 이미 클래스, 메서드 단위의 필터 기능을 가지고 있기 때문에, 프록시 적용 대상 여부를 정밀하게 설정 할 수 있다.
참고로 어드바이저는 포인트컷을 가지고 있다. 따라서 어드바이저를 통해 포인트컷을 확인할 수 있다.
뒤에서 학습하겠지만 스프링 AOP는 포인트컷을 사용해서 프록시 적용 대상 여부를 체크한다.
결과적으로 포인트컷은 다음 두 곳에 사용된다.
스프링이 제공하는 빈 후처리기 1
주의 - 다음을 꼭 추가해주어야 한다.
build.gradle - 추가
이 라이브러리를 추가하면
aspectjweaver라는aspectJ관련 라이브러리를 등록하고, 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다. 스프링 부트가 없던 시절에는@EnableAspectJAutoProxy를 직접 사용 해야 했는데, 이 부분을 스프링 부트가 자동으로 처리해준다.aspectJ는 뒤에서 설명한다. 스프링 부트가 활성화하는 빈은AopAutoConfiguration를 참고하자. (스프링 부트가 이걸 자동으로 켜줌)결국 뭐가 최종적으로 되느냐?
자동 프록시 생성기가 활성화 된다 (스프링빈에 등록된다)
자동 프록시 생성기 - AutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator라는 빈 후처리기가 스프링 빈에 자동으로 등록된다.Advisor들을 자동으로 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다.Advisor안에는Pointcut과Advice가 이미 모두 포함되어 있다. 따라서Advisor만 알고 있으면 그 안에 있는Pointcut으로 어떤 스프링 빈에 프록시를 적용해야 할지 알 수 있다. 그리고Advice로 부가 기능을 적용하면 된다. 그래서 Advisor 만 찾으면 된다!참고
AnnotationAwareAspectJAutoProxyCreator 의 아주 위쪽 부모에 BeanPostProcessor 가 있음.
1. 생성: 스프링이 스프링 빈 대상이 되는 객체를 생성한다. (
@Bean, 컴포넌트 스캔 모두 포함)2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
3. 모든 Advisor 빈 조회: 자동 프록시 생성기 - 빈 후처리기는 스프링 컨테이너에서 모든
Advisor를 조회한다.4. 프록시 적용 대상 체크: 앞서 조회한
Advisor에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다. 이때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭해본다. 그래서 조건이 하나라도 만족하면 프록시 적용 대상이 된다. 예를 들어서 10개의 메서드 중에 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 된다.5. 프록시 생성: 프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록한다. 만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록한다.
6. 빈 등록: 반환된 객체는 스프링 빈으로 등록된다.
포인트컷을 두개를 구분해야하는데
적용해보자
이거만 있으면 끝나는데, 프록시를 생성하기 위한
AnnotationAwareAspectJAutoProxyCreator가 이미 빈으로 등록되어있고,앞서 말했듯이 advisor 를 찾아내 알아서 적용시켜 프록시를 만들어버리기 때문.
실행해보자
모두 잘 적용된다.
중요: 포인트컷은 2가지에 사용된다.
1. 프록시 적용 여부 판단 - 생성 단계
orderControllerV1은request(),noLog()가 있다. 여기에서request()가 조건에 만족하므로 프록시를 생성한다.2. 어드바이스 적용 여부 판단 - 사용 단계
orderControllerV1은 이미 프록시가 걸려있다.orderControllerV1의request()는 현재 포인트컷 조건에 만족하므로 프록시는 어드바이스를 먼저 호출하고,target을 호출한다.orderControllerV1의noLog()는 현재 포인트컷 조건에 만족하지 않으므로 어드바이스를 호출하지 않고 바로target만 호출한다.참고: 프록시를 모든 곳에 생성하는 것은 비용 낭비이다. 꼭 필요한 곳에 최소한의 프록시를 적용해야 한다. 그래서 자동 프록시 생성기는 모든 스프링 빈에 프록시를 적용하는 것이 아니라 포인트컷으로 한번 필터링해서 어드바이스가 사용될 가능성이 있는 곳에만 프록시를 생성한다.
스프링이 제공하는 빈 후처리기2
애플리케이션 로딩 로그에 뭔가 남아있다
애플리케이션 서버를 실행해보면, 스프링이 초기화 되면서 기대하지 않은 이러한 로그들이 올라온다. 그 이유는 지금 사용한 포인트컷이 단순히 메서드 이름에
"request*", "order*", "save*"만 포함되어 있으면 매칭 된다고 판단하기 때문이다.결국 스프링이 내부에서 사용하는 빈에도 메서드 이름에
request라는 단어만 들어가 있으면 프록시가 만들어지고 되고, 어드바이스도 적용되는 것이다.결론적으로 패키지에 메서드 이름까지 함께 지정할 수 있는 매우 정밀한 포인트컷이 필요하다.
AspectJExpressionPointcut
AspectJ라는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다. AspectJ 포인트컷 표현식과 AOP는 조금 뒤에 자세히 설명하겠다. 지금은 특별한 표현식으로 복잡한 포인트컷을 만들 수 있구나 라고 대략 이해하면 된다.
기존 컨피그에 아래 코드 추가
이렇게 하면 hello.proxy.app 여기 위치만 프록시 적용 대상이 된다.

AspectJExpressionPointcut: AspectJ 포인트컷 표현식을 적용할 수 있다.execution(* hello.proxy.app..*(..)): AspectJ가 제공하는 포인트컷 표현식이다. 이후 자세히 설명하겠다. 지금은 간단히 알아보자.*: 모든 반환 타입hello.proxy.app..: 해당 패키지와 그 하위 패키지*(..):*모든 메서드 이름,(..)파라미터는 상관 없음쉽게 이야기해서
hello.proxy.app패키지와 그 하위 패키지의 모든 메서드는 포인트컷의 매칭 대상이 된다.문제점 : no-log 요청해도 로그가 찍힌다.
advisor2에서는 단순히package를 기준으로 포인트컷 매칭을 했기 때문이다.표현식을 다음과 같이 수정했다.
&&: 두 조건을 모두 만족해야 함||: or 조건!: 반대hello.proxy.app패키지와 하위 패키지의 모든 메서드는 포인트컷에 매칭하되,noLog()메서드는 제외하라는 뜻이다.이제 no-log 요청시 로그가 남지 않는다.
하나의 프록시, 여러 Advisor 적용
예를 들어서 어떤 스프링 빈이
advisor1,advisor2가 제공하는 포인트컷의 조건을 모두 만족하면 프록시 자동 생성기는 프록시를 몇 개 생성할까?프록시 자동 생성기는 프록시를 하나만 생성한다. 왜냐하면 프록시 팩토리가 생성하는 프록시는 내부에 여러
advisor들을 포함할 수 있기 때문이다. 따라서 프록시를 여러 개 생성해서 비용을 낭비할 이유가 없다.프록시 생성 상황별 정리
advisor1의 포인트컷만 만족 : 프록시 1개 생성, 프록시에advisor1만 포함advisor1,advisor2의 포인트컷 모두 만족 : 프록시 1개 생성, 프록시에advisor1,advisor2모두 포함advisor1,advisor2의 포인트컷 모두 만족 X : 프록시 생성되지 않음.이후에 설명할 스프링 AOP도 동일한 방식으로 동작한다. (프록시는 여러개 생성되지 않는다!)
All reactions