implementation 'org.springframework.boot:spring-boot-starter-aop'
이 라이브러리를 추가하면 aspectjweaver라는 aspectJ 관련 라이브러리를 등록하고 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다.
스프링 부트가 없던 시절에는 @EnableAspectJWutoProxy를 직접 사용해야 했는데 이 부분을 스트림 부트가 자동으로 처리해준다.
스프링 부트가 활성화 하는 빈은 AopAutoConfiguration를 참고하자.
- 앞서 이야기한 스프링 부트 자동 설정으로 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기가 스프링 빈에 자동으로 등록된다.
- 이름 그대로 자동으로 프록시를 생성해주는 빈 후처리기이다.
- 이 빈 후처리기는 스프링 빈으로 등록된 Advisor들을 자동으로 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다.
Advisor 안에는 Pointcut과 Advice가 이미 모두 포함되어 있다. 따라서 Advisor만 알고 있으면 그 안에 있는 Pointcut으로 어떤 스프링 빈에 프록시를 적용해야 할지 알 수 있다.
그리고 Advice로 부가 기능을 적용하면 된다.
AnnotationAwareAspectJAutoProxyCreator는 @AspectJ와 관련된 AOP 기능도 자동으로 찾아서 처리해준다.
Advisor는 물론이고 @Aspect도 자동으로 인식해서 프록시를 만들고 AOP를 적용해준다.
- 생성 : 스프링이 스프링 빈 대상이 되는 객체르르 생성한다. (Bean, 컴포넌트 스캔 모두 포함)
- 전달 : 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
- 모든 Advisor 빈 조회 : 자동 프록시 생성기 - 빈 후처리기는 스프링 컨테이너에서 모든 Advisor를 조회한다.
- 프록시 적용 대상 체크 : 앞서 조회한 Advisor에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다.
이 때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메소드를 포인트컷에 하나하나 모두 매칭해본다.
그래서 조건이 하나라도 만족하면 프록시 적용 대상이 된다.
예를 들어서 10개의 메소드 중에 하나만 포인트 컷 조건에 만족해도 프록시 적용 대상이 된다.- 프록시 생성 : 프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록한다.
만약 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록한다.- 빈 등록 : 반환된 객체는 스프링 빈으로 등록된다.
프록시는 내부에 Advisor와 실체 호출할 대상 객체(target)을 알고 있다.
@Slf4j
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AutoProxyConfig {
@Bean
public Advisor advisor1(LogTrace logTrace) {
//pointcut
final NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
//advice
final LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
프록시는 AutoProxyCreator 에서 자동으로 생성해주기 때문에 Advisor만 빈으로 등록하면 된다.
- 자동 프록시 생성기는 포인트컷을 사용해서 해당 빈이 프록시를 생성할 필요가 있는지 없는지 체크한다.
- 클래스 + 메소드 조건을 모두 비교한다.
이때 모든 메소드를 체크하는데 포인트컷 조건에 하나하나 매칭해본다.
만약 조건에 맞는 것이 하나라도 있으면 프록시를 생성한다.
ex) orderController에 aaa(),bbb() 가 있다고 가정했을 때 aaa()의 조건에 만족하면 프록시를 생성한다.- 만약 조건에 맞는 것이 하나도 없으면 프록시를 생성할 필요가 없으므로 프록시를 생성하지 않는다.
- 프록시가 호출되었을 때 부가 기능은 Advice를 적용할지 말지 포인트컷을 보고 판단한다.
- 1에서 orderController는 이미 프록시에 걸려있다.
- orderController의 aaa()는 포인트컷 조건에 만족하므로 프록시는 Advice를 먼저 호출하고 target을 호출한다.
- orderController의 bbb()는 현재 포인트컷 조건에 만족하지 않으므로 Advice를 호출하지 않고 바로 target을 호출한다.
프록시를 모든 곳에 생성하는 것은 비용 낭비이다.
꼭 필요한 곳에 최소한의 프록시를 적용해야 한다.
그래서 자동 프록시 생성기는 모든 스프링 빈에 프록시를 적용하는 것이 아니라 포인트컷으로 한번 필터링해서 Advice가 상요될 가능성이 있는 곳에만 프록시를 생성한다.
포인트 컷을 정밀하게 설정하지 않으면 기대하지 않았던 부분에도 프록시가 걸릴 수 있다.
따라서 패키지에 메소드 이름까지 함꼐 짖어할 수 있는 매우 정밀한 포인트 컷이 필요하다.
AspectJ라는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다.
@Bean
public Advisor advisor2(LogTrace logTrace) {
//pointcut
final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..))");
//advice
final LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
execution(* hello.proxy.app..*(..))
: AspectJ가 제공하는 포인트컷 표현식이다.
*
: 모든 반환 타입
hello.proxy.app..
: 해당 패키지와 그 하위 패키지
*(..)
:*
모든 메소드 이름,(..)
: 파라미터는 상관 없음
예를 들어서 어떤 스프링 빈이 advisor1, advisor2가 제공하는 포인트컷 조건을 모두 만족하면 프록시 자동 생성기는 프록시를 몇개 생성할가?
--> 프록시 자동 생성기는 프록시 하나만 생성한다.
왜냐하면 프록시 팩토리가 생성하는 프록시는 내부에 여러 advisor들을 포함할 수 있기 때문이다.
따라서 프록시를 여러개 생성해서 비용을 낭비할 이유가 없다.
- advisor1의 포인트컷만 만족 --> 프록시 1개 생성, 프록시에 advisor1만 포함
- advisor1, advisor2의 포인트컷을 모두 만족 -> 프록시 1개 생성, 프록시에 advisor1, advisor2 모두 포함
- advisor1, advisor2의 포인트컷을 모두 만족하지 않음 -> 프록시가 생성되지 않음
자동 프록시 생성기인 AnnotationAwareAspectJAutoProxyCreator 덕분에 개발자는 매우 편리하게 프록시를 적용할 수 있다.
이제 Advisor만 스프링 빈으로 등록하면 된다.
Advisor = Pointcut + Advice