Spring Proxy Factory
Java에서 Proxy사용하는 방법에 대해서 두가지 했었음
1. Jdk 동적 Proxy
>> Interface가 있어야함. InvocationHandler를 구현해서 객체 생성 후 Proxy를 생성해서 사용
https://tlqckd0.tistory.com/102
2. CGLIB
>> 인터페이스없이 구체 클레스(인터페이스도 됨)를 사용. MethodInterceptor를 구현 후 Enhancer에 등록 후 Proxy 생성
https://tlqckd0.tistory.com/103
이렇게 두가지 했었음.
1. Spring이 제공하는 ProxyFactory
spring은 위에 두가지(interface인 경우 JDK동적프록시, 구체클레스인 경우 CGLIB) 기능을 편하게 사용할 수 있는
ProxyFactory를 제공한다.
이때에도 MethodInterceptor를 구현해야 하는데 이거는 CGLIB이랑 다른 package임
import org.aopalliance.intercept.MethodInterceptor; // << 이거 해야함
import org.springframework.cglib.proxy.MethodInterceptor; // << 이거 아님;;
일단 MethodInterceptor를 구현해서 Advice를 만들면 되는데 이 때 invoke <ㅡ 이놈은 invocation.proceed()로 수행함.
그리고 이거는 실제 호출 대상을 ProxyFactory생성 시점에 넣어주기 때문에 그거를 위한 작업은 따로 안해도 됨
@Slf4j
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("Proxy 실행");
Object result = invocation.proceed(); // invoke -> target을 찾아서 호출해줌.
log.info("TimeProxy 종료 ");
return result;
}
}
이렇게 한 다음 실행 시키려면 ProxyFactory 생성 + Advice 등록만 하면 된다.
void interfaceProxy(){
ServiceInterface target = new ServiceImpl();
// 1. ProxyFactory 생성(호출 클래스 넣어줌)
ProxyFactory proxyFactory = new ProxyFactory(target);
//proxyFactory.setProxyTargetClass(true); // -> 항상 CGLIB 기반
// 2. Advice 등록
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("target class = {}", target.getClass());
log.info("proxy class = {}", proxy.getClass());
proxy.save();
Assertions.assertThat(AopUtils.isAopProxy(proxy)).isTrue();
Assertions.assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue();
Assertions.assertThat(AopUtils.isCglibProxy(proxy)).isFalse();
}
이렇게 하고 실행시키면 아래와 같은 결과가 나온다.
2. Advice, Pointcut, Advisor
Advice(MethodInterceptor구현)는 로직, Pointcut은 필터이고 이거를 합치면 Advisor임..(비문학 못함)
이거를 직접 생성하는 경우도 있겠지만 그럴리 없다고함.. Spring에서 다 제공해주기 때문에
Advisor의 경우 DefaultPointcutAdvisor를 사용하면 됨
void advisorTest3(){
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// 1. Pointcut 생성
NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
// method 이름이 save 인 경우에만 Advice(proxy)적용
nameMatchMethodPointcut.setMappedName("save");
// 2. Advisor 생성 (Pointcut, advice) 주입
DefaultPointcutAdvisor advisor
= new DefaultPointcutAdvisor(nameMatchMethodPointcut, new TimeAdvice());
// 3. ProxyFactory 에 Advisor 등록
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find(); // 이건 proxy 적용 X
}
이렇게 작성을 하면 method이름이 save인 것만 advise가 적용되고 아래와 같은 경과가 나옴
3. Advisor 여러개 적용
void multiProxy2(){
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
//넣은 순서대로 호출해줌 ..
proxyFactory.addAdvisor(advisor2);
proxyFactory.addAdvisor(advisor1);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
}
그냥 이렇게 등록해주면 되는데 이러는 경우 등록한 순서대로 호출이 됨.
Advisor를 Bean으로 등록해두면 자동으로 사용이 됨..
@Bean
public Advisor advisor(Object obj) {
//pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(" aop .. ");
//advice
AdviceLogic advice = new AdviceLogic(obj);
return new DefaultPointcutAdvisor(pointcut, advice);
}