프록시를 사용하는 프록시 패턴, 데코레이터 패턴을 배운 이후
프록시 클래스들의 소스 코드는 거의 동일한 모양을 하고 있는데 이 프록시를 적용하기 위해서
계속 프록시 클래스들을 만들어야 하는 문제점이 있었다.
동적 프록시 기술을 이용해서 쉽게 적용시켜보자!
바이트코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리 이다.
인터페이스가 없어도 구체 클래스만 가지고 동적 프록시를 만들어낼 수 있다.
참고로 우리가 CGLIB를 직접 사용하는 경우는 거의 없다고 하신다. 스프링의
ProxyFactory
라는 것이 이 기술을 편하게 사용하게 도와주기 때문이라고 한다.
그러니까 간단하게 알아보도록 하자.
구체 클래스만 있는 서비스 클래스를 사용하자.
@Slf4j
public class ConcreteService {
public void call() {
log.info("ConcreteService 호출");
}
}
JDK 동적 프록시가 InvocationHandler
를 제공했듯이,
CGLIB은 MethodInterceptor
를 제공한다.
@Slf4j
public class TimeMethodInterceptor implements MethodInterceptor {
private final Object target;
public TimeMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
log.info("TimeProxy 실행");
long startTime = System.currentTimeMillis();
Object result = proxy.invoke(target, args);
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy 종료 resultTime={}", resultTime);
return result;
}
}
ConcreteService
와 TimeMethodInterceptor
를 만들었다.
이제 Test Code로 CGLIB를 사용해보자.
@Test
void cglib() {
ConcreteService target = new ConcreteService();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ConcreteService.class);
enhancer.setCallback(new TimeMethodInterceptor(target));
ConcreteService proxy = (ConcreteService)enhancer.create();
log.info("targetClass={}", target.getClass()); //targetClass와
log.info("proxyClass={}", proxy.getClass()); //proxyClass를 알아보기 위해 남긴 로그
proxy.call();
}
이 코드도 마찬가지로 proxy.call()
이 실제 로직이고 나머지는 설정이다.
이렇게 해서 구체 클래스에서도 동적으로 프록시를 만들어서 적용할 수 있었다.
프록시가 동작은 이해하겠는데 코드는 어렵다. 완강 후 다시 들을날을 기약하며....