동적 프록시의 문제점
스프링은 동적 프록시를 통합해서 편리하게 만들어주는 프록시 팩토리 라는 기능을 제공한다.
프록시 팩토리는 인터페이스가 있으면 JDK동적프록시를 사용하고 구체 클래스만 있으면 CGLIB를 지원한다.
클라이언트가 프록시팩토리로 요청을 하면 프록시 팩토리가 상황에 따라서 jdk동적프록시나 CGLIB구체클래스 프록시로 반환해준다.
ServiceInterface target = new ServiceImpl();
//여기서 프록시를 만들때 타캣을 넣어준다.
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
//프록시팩토리를 사용할 때만 사용가능
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue();
assertThat(AopUtils.isCglibProxy(proxy)).isFalse();
proxyFactory.getProxy(): 프록시 객체를 생성하고 그 결과를 받는다.
위와같이 해도되고 인스턴스에 직접 클래스정보를 출력해서 확인할 수 있다 .getClass();
프록시 팩토리의 서비스 추상화 덕분에 구체적인 프록시기술에 의존하지 않고 매우 편리하게 동적프록시를 생성할 수 있다.
내부적으로 핸들링이 돼 있기 때문이다.
스프링 부트는 AOP를 적용할때 기본적으로 'proxyTargetClass=ture'로 설정해서 사용한다.
따라서 인터페이스가 있어도 항상 CGLIB를 사용해서 구체클래스를 기반으로 프록시를 생성한다.
예를 들어 부가 기능 로직을 적용해야하는데 포인트컷으로 어디에 적용할지 선택하고,
어드바이스로 어떤 로직을 적용할지 선택하는 것이다.
어디와 어떤 로직을 모두 알고 있는것이 어드바이저 이다.
이렇게 구분한 이유 역할과 책임을 명확하게 분리한 것이다.
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
스프링은 무수히 많은 포인트 컷을 제공한다.
대표적인 몇가지
여기에서 실무에서 사용하기도 편리하고 기능도 가장 많은 aspectJ표현식을 기반으로 사용하는
AspectJExpressionPointcut을 사용하게 된다.
어드바이저는 하나의 포인트컷과 하나의 어드바이스를 가지고 있다. 여러 어드바이저를 하나의 target에 적용하려면?
프록시를 여러개를 만드는 방법이 있지만 좋지않다.
@Test
@DisplayName("하나의 프록시 여러 어드바이저")
void multiAdvisorTest2(){
//client -> proxy->advisor1 -> advisor2 -> target
//여러 어드바이저 적용
//이때 각 어드바이저의 포인트컷이 적용된상태로 실행된다.
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
//프록시 1 생성
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory1 = new ProxyFactory(target);
//순서대로 동작
proxyFactory1.addAdvisor(advisor2);
proxyFactory1.addAdvisor(advisor1);
ServiceInterface proxy1 = (ServiceInterface) proxyFactory1.getProxy();
//실행
proxy1.save();
스프링 AOP를 처음 공부하거나 적용하면 AOP적용 수 만큼 프록시가 생성된다고 착각하게 된다.
스프링은 AOP를 적용할때 최적화를 진행해서 지금처럼 프록시는 하나만 만들고, 하나의 프록시에 여러 어드바이저를 적용한다.
정리하면 하나의 target에 여러 AOP가 동시에 적용되어도 스프링은 AOP는 target마다 하나의 프록시만 생성하고
그 안에 여러 어드바이저를 넣는다는것이다.