CGLIB
바이트코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리입니다.
CGLIB을 사용하면 인터페이스가 없어도 구체 클래스만 가지고 동적 프록시를 만들 수 있습니다.
CGLIB은 외부 라이브러리로, 스프링 프레임워크 내부 소스 코드에 포함되어 있기 때문에 스프링을 사용한다면 라이브러리 추가 없이 사용할 수 있습니다.
CGLIB 구현
NonInteferfaceService
인터페이스가 없는 구현체 서비스를 만들었습니다.
@Slf4j
public class NonInterfaceService {
public void call() {
log.info("NonInterfaceService call");
}
}
ExecuteTimeMethodInterceptor
CGLIB을 사용하기 위해 MethodInterceptor를 상속받아 CGLIB 프록시의 실행 로직을 정의합니다.
@Slf4j
public class ExecuteTimeMethodInterceptor implements MethodInterceptor {
/**
* CGLIB 프록시의 실행 로직을 정의합니다.
*/
private final Object target;
public ExecuteTimeMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj/*프록시가 호출할 실제 대상*/,
Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
log.info("ExecuteTimeProxy Execute");
long startTime = System.currentTimeMillis();
//methodProxy를 사용하면 method를 사용하는 것보다 성능이 좋다고 합니다.
Object result = methodProxy.invoke(target, args);
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
log.info("ExecuteTimeProxy Terminate executeTime={}", executeTime);
return result;
}
}
CglibEX
실제로 CGLIB을 이용해서 프록시 객체를 만들고 로그를 찍어보는 테스트
@Slf4j
public class CglibEX {
@Test
void cglibEX() {
// 인터페이스가 없는 클래스도 프록시 객체를 생성할 수 있는지 테스트합니다.
NonInterfaceService target = new NonInterfaceService();
// CGLIB를 생성하는 코드입니다.
Enhancer enhancer = new Enhancer();
// CGLIB를 이용해 동적 프록시를 만들어야하는데 인터페이스가 없기 때문에
// 구체 클래스를 기반으로 NonInterfaceService를 상속받은 프록시를 만들어야 합니다.
// 그래서 NonInterfaceService 클래스를 지정해줍니다.
enhancer.setSuperclass(NonInterfaceService.class);
// 프록시가 실행할 로직을 지정해주는 코드입니다.
enhancer.setCallback(new ExecuteTimeMethodInterceptor(target));
// CGLIB이 생성한 프록시 객체가 반환됩니다.
NonInterfaceService proxy = (NonInterfaceService) enhancer.create();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.call();
}
}
출력 결과
11:21:58.943 [Test worker] INFO hello.proxy.cglib.CglibEX - targetClass=class hello.proxy.common.service.NonInterfaceService
11:21:58.948 [Test worker] INFO hello.proxy.cglib.CglibEX - proxyClass=class hello.proxy.common.service.NonInterfaceService$$EnhancerByCGLIB$$4c43754e
11:21:58.948 [Test worker] INFO hello.proxy.cglib.code.ExecuteTimeMethodInterceptor - ExecuteTimeProxy Execute
11:21:58.961 [Test worker] INFO hello.proxy.common.service.NonInterfaceService - NonInterfaceService call
11:21:58.962 [Test worker] INFO hello.proxy.cglib.code.ExecuteTimeMethodInterceptor - ExecuteTimeProxy Terminate executeTime=14
결론
CGLIB이 어떤방식으로 동작하는지 알아보았습니다.
CGLIB이 스프링에서 많이 사용되는 것으로 알고있는데
앞으로는 CGLIB이 생성한 프록시 객체를 보게되면 반가울 것 같습니다.
포스팅 끝까지 봐주셔서 감사합니다.
[참고]
'Spring' 카테고리의 다른 글
[Spring] Netty Server & Client 구현 (4) | 2022.11.23 |
---|---|
[Spring] JPA Fetch Join (0) | 2022.09.05 |
[Spring] Server-Client 간 암, 복호화 구현 (0) | 2022.08.18 |
[Spring] JWT Token 인증 구현 (0) | 2022.08.18 |
[Spring] Netty 서버 데이터 끊기는 문제 해결 (0) | 2022.08.16 |