오봉이와 함께하는 개발 블로그
스프링 핵심 원리 - 고급편 > 여러 어드바이저 함께 적용 본문
스프링 핵심 원리 - 고급편 > 여러 어드바이저 함께 적용
어드바이저는 하나의 포인트컷과 하나의 어드바이저를 가지고 있다.
만약 여러 어드바이저를 하나의 target에 적용하려면 어떻게 해야할까?
가장 당장 떠오르는 방법은 프록시를 여러 개 만들면 될 거 같다.
public class MultiAdvisorTest {
@Test
void multiAdvisorTest1() {
// client -> proxy2(advisor2) -> proxy1 (advisor1) -> target
// Create proxy1
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory1 = new ProxyFactory(target);
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
proxyFactory1.addAdvisor(advisor1);
ServiceInterface proxy1 = (ServiceInterface) proxyFactory1.getProxy();
ProxyFactory proxyFactory2 = new ProxyFactory(proxy1);
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
proxyFactory2.addAdvisor(advisor2);
ServiceInterface proxy2 = (ServiceInterface) proxyFactory2.getProxy();
proxy2.save();
}
@Slf4j
static class Advice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("Call advice1");
return invocation.proceed();
}
}
@Slf4j
static class Advice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("Call advice2");
return invocation.proceed();
}
}
}
위 코드는 런타임에 아래 그림과 같이 동작한다.
실행 로그
23:02:01.578 [main] INFO hello.proxy.advisor.MultiAdvisorTest$Advice2 - Call advice2
23:02:01.579 [main] INFO hello.proxy.advisor.MultiAdvisorTest$Advice1 - Call advice1
23:02:01.579 [main] INFO hello.proxy.common.service.ServiceImpl - call save
포인트컷은 모두 항상 true를 반환하도록 설정했기 때문에 둘 다 어드바이스가 적용된다.
여러 프록시의 문제
위 방법이 잘못된 것은 아니지만, 프록시를 2번 생상해야 한다는 문제가 있다. 만약 어드바이저가 10개라면 10개의 프록시를 생성해야 한다는 문제가 있다.
더군다나 10개에 순서가 중요하다면 피로감이 든다.
하나의 프록시, 여러 어드바이저
스프링은 이 문제를 해결하기 위해 하나의 프록시에 여러 어드바이저를 적용할 수 있게 만들어두었다.
@Test
void multiAdvisorTest2() {
// proxy -> advisor2 -> advisor1 -> target
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory1 = new ProxyFactory(target);
proxyFactory1.addAdvisor(advisor2);
proxyFactory1.addAdvisor(advisor1);
ServiceInterface proxy = (ServiceInterface) proxyFactory1.getProxy();
proxy.save();
}
- 프록시 팩토리에 원하는 만큼
addAdvisor()
를 통해서 어드바이저를 등록하면 된다. - 등록하는 순서대로
advisor
가 호출된다. 여기서는advisor2
,advisor1
순서로 등록했다.
실행 로그
23:07:42.605 [main] INFO hello.proxy.advisor.MultiAdvisorTest$Advice2 - Call advice2
23:07:42.606 [main] INFO hello.proxy.advisor.MultiAdvisorTest$Advice1 - Call advice1
23:07:42.606 [main] INFO hello.proxy.common.service.ServiceImpl - call save
중요
스프링의 AOP를 처름 공부하거나, 사용하면 AOP 적용 수 만큼 프록시가 생성된다고 착각하게 된다. 실제 많은 실무 개발자들도 이렇게 생각한다고 한다.
스프링은 AOP를 적용할 때, 최적화를 진행해서 지금처럼 프록시는 하나만 만들고, 하나의 프록시에 여러 어드바이저를 적용한다.
정리하면 하나의 target에 여러 AOP가 적용되어도, 스프링의 AOP는 target마다 하나의 프록시만 생성한다. 이 부분을 꼭 기억해두자.
코드에서도 볼 수 있듯, addAdvisor
라는 이름을 사용하는 것이 힌트가 될 수도 있겠다. 실제 코드에서 advisors는 private List<Advisor> advisors;
형태로 적용되어 있고, List인 것도 이유중 하나가 될 수 있을 것 같다.
출처: 김영한 지식공유자의 스프링 핵심 원리 고급편
'BE > Spring' 카테고리의 다른 글
스프링 핵심 원리 - 고급편 > 빈 후처리기 소개 (0) | 2024.10.06 |
---|---|
스프링 핵심 원리 - 고급편 > 프록시 팩토리 적용 1, 2, 정리 (0) | 2024.10.03 |
스프링 핵심 원리 - 고급편 > 스프링이 제공하는 포인트컷 (0) | 2024.10.03 |
스프링 핵심 원리 - 고급편 > 직접 만든 포인트컷 (3) | 2024.10.01 |
스프링 핵심 원리 - 고급편 > 어드바이저 (0) | 2024.10.01 |