내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.
5. 스프링이 제공하는 빈 후처리기1
스프링이 제공하는 자동 후처리기를 사용하기 위해서는, 다음 의존성을 꼭 추가해야줘야 한다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
라이브러리를 추가하면 aspectjweaver 라는 aspectJ 관련 라이브러리를 등록하고, 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다.
▶ (AnnotationAwareAspectJ)AutoProxyCreator
스프링 부트 자동 설정으로 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기가 스프링 빈에 자동으로 등록된다.
AutoProxyCreator는 스프링 빈으로 등록된 Advisor들을 자동으로 찾아서 Proxy를 생성해서 등록해주는 후처리기 이다.
Advisor 안에는 Pointcut 과 Advice 가 이미 모두 포함되어 있다.
따라서 Advisor 만 알고 있으면 그 안에있는 Pointcut으로 어떤 스프링빈 에 프록시를 적용해야 할지 알 수 있다.
그리고 Advice로부가 기능을 적용하면 된다.
추가적으로 @AspectJ와 관련된 AOP 기능도 자동으로 찾아서 처리해준다. 이는 나중에 AOP에서 더 알아보자.
자동 프록시 생성과정을 우선 그림으로 살펴보자.
1. 생성: 스프링이 스프링 빈 대상이 되는 객체를 생성한다. ( @Bean , 컴포넌트 스캔 모두 포함 )
2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
3. 모든 Advisor 빈 조회: 자동 프록시 생성기(빈 후처리기)는 스프링 컨테이너에서 모든 Advisor 를 조회한다.
4. 프록시 적용 대상 체크
앞서 조회한 Advisor 에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다.
이때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭해본다.
조건이 하나라도 만족하면 프록시 적용 대상이 된다.
예를 들어서 10개의 메서드 중에 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 된다.
5. 프록시 생성
프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록한다.
만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록한다.
6. 빈 등록: 반환된 객체는 스프링 빈으로 등록된다.
프록시 내부에는 실제로 호출하게 되는 target과 advisor에 대한 정보가 담겨있다.
코드를 통해서 적용시켜보자. 다음과 같이 Config 파일에 Advisor를 빈으로만 등록시켜주면 된다.
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AutoProxyConfig {
@Bean
public Advisor advisor1(LogTrace logTrace) {
// pointCut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
// advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
빈 후처리기는 따로 등록하지 않아도 된다.
스프링은 자동 프록시 생성기라는 ( AnnotationAwareAspectJAutoProxyCreator ) 빈 후처리기를 자동으로 등록해준다.
이후 설정파일에 다음과 같이 @Import문만 추가해주면 된다.
@Import(AutoProxyConfig.class)
이후 애플리케이션을 실행시키면 프록시가 적용된 결과를 확인할 수 있다.
포인트컷은 2가지에 사용된다.
1. 프록시 적용 여부 판단 - 생성 단계
자동 프록시 생성기는 포인트컷을 사용해서 해당 빈이 프록시를 생성할 필요가 있는지 없는지 체크한다.
클래스 + 메서드 조건을 모두 비교한다.
이때 모든 메서드를 체크하는데, 포인트컷 조건에 하나하나 매칭해본다.
만약 조건에 맞는 것이 하나라도 있으면 프록시를 생성한다.
예) orderControllerV1 은 request() , noLog() 가 있다. 여기에서 request() 가 조건에 만족하므로 프록시를 생성한다.
2. 어드바이스 적용 여부 판단 - 사용 단계
프록시가 호출되었을 때 부가 기능인 Advice를 적용할지 말지 포인트컷을 보고 판단한다.
앞서 설명한 예에서 orderControllerV1 은 이미 프록시가 걸려있다.
orderControllerV1 의 request() 는 현재 포인트컷 조건에 만족하므로 프록시는 어드바이스를 먼저 호출하고, target 을 호출한다.
orderControllerV1 의 noLog() 는 현재 포인트컷 조건에 만족하지 않으므로 어드바이스를 호출하지 않고 바로 target 만 호출한다.
포인트컷으로 필터링 해서 어드바이스가 사용될 가능성이 있는 곳에만 프록시를 생성한다.
6. 스프링이 제공하는 빈 후처리기2
직전 단락에서 만든 Config를 등록하여 실행하면, 한가지 이상한점이 있다.
원하지 않는 부분까지 실행시간이 측정되고 있다.
request*, order* 이라는 문자열 매칭 때문에 출력되버리는 것 이다.
그 이유는 지금 사용한 포인트컷이 단순히 메서드 이름에 "request*", "order*", "save*" 만 포함되어 있으면 매칭 된다고 판단하기 때문이다.
스프링이 내부에서 사용하는 빈에도 메서드 이름에 request 라는 단어만 들어가 있으면 프록시가 만들어지고 되고, 어드바이스도 적용되는 것이다.
패키지에 메서드 이름까지 함께 지정할 수 있는 매우 정밀한 포인트컷이 필요하다.
▶ AspectJExpressionPointcut
AspectJ라는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다. AspectJ 포인트컷 표현식과 AOP는 조금 뒤에 자세히 설명하겠다.
지금은 특별한 표현식으로 복잡한 포인트컷을 만들 수 있구나 라고 대략 이해하면 된다.
@Bean
public Advisor advisor2(LogTrace logTrace) {
// pointCut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..))");
// advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
AspectJ 포인트컷 표현식을 사용하는 pointcut을 만들 수 있게 되었다.
execution(* hello.proxy.app..*(..))
위 표현식을 간단하게 의미를 살펴보면
- * : 모든 반환 타입
- hello.proxy.app.. : 해당 패키지와 그 하위 패키지
- *(..) : * 모든 메서드 이름, (..) 파라미터는 상관 없음
쉽게 이야기해서 hello.proxy.app 패키지와 그 하위 패키지의 모든 메서드는 포인트컷의 매칭 대상이 된다.
실행시 정상적으로 원하는 결과가 나온다.
다만, "http://localhost:8080/v1/no-log" 의 경우 로그가 출력되면 안되는데 출력되고 있다.
advisor2 에서는 단순히 package 를 기준으로 포인트컷 매칭을 했기 때문이다.
이를 수정한 advisor3 는 다음과 같다.
@Bean
public Advisor advisor3(LogTrace logTrace) {
// pointCut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))" );
// advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
표현식이 조금 수정되었다.
- && : 두 조건을 모두 만족해야 함
- ! : 반대
hello.proxy.app 패키지와 하위 패키지의 모든 메서드는 포인트컷의 매칭하되, noLog() 메서드는 제외하라는 뜻이다.
이번에는 "http://localhost:8080/v1/no-log" 에 요청을 보내도 로그가 남지 않는것을 확인할 수 있다.
7. 하나의 프록시, 여러 Advisor 적용
예를 들어서 어떤 스프링 빈이 advisor1 , advisor2 가 제공하는 포인트컷의 조건을 모두 만족하면 프록시 자동 생성기는 프록시를 몇 개 생성할까?
프록시 자동 생성기는 프록시를 하나만 생성한다.
왜냐하면 프록시 팩토리가 생성하는 프록시는 내부에 여러 advisor 들을 포함할 수 있기 때문이다.
▶ 프록시 자동 생성기 상황별 정리
- advisor1 의 포인트컷만 만족 프록시1개 생성, 프록시에 advisor1 만 포함
- advisor1 , advisor2 의 포인트컷을 모두 만족 프록시1개 생성, 프록시에 advisor1 , advisor2 모두 포함
- advisor1 , advisor2 의 포인트컷을 모두 만족하지 않음 프록시가 생성되지 않음
다음 글 에서는 @Aspect 애노테이션을 사용해서 더 편리하게 포인트컷과 어드바이스를 만들고 프록시를 적용해보자.
'BackEnd > Spring' 카테고리의 다른 글
[Spring] 스프링 AOP 개념 (0) | 2022.08.17 |
---|---|
[Spring] @Aspect AOP (0) | 2022.08.16 |
[Spring] 빈 후처리기 - 1 (0) | 2022.08.15 |
[Spring] 스프링이 지원하는 프록시 - 2 (0) | 2022.08.14 |
[Spring] 스프링이 지원하는 프록시 - 1 (0) | 2022.08.14 |
댓글