CGLIB (Code Generation Library)는 주로 클래스의 구현체를 대상으로 동적 프록시를 생성하는 데 사용됩니다.

 

jdk 동적 프록시에 Invokehandler가 있다면, CGLIB에는 MethodInterceptor가 있다.

package hello.proxy.cglib.code;


import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
@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 methodProxy) throws Throwable {
        log.info("TimeProxy 실행");
        long startTime = System.currentTimeMillis();

        Object result = methodProxy.invoke(target, args);

        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeProxy 종료 resultTime={}",resultTime);
        return result;
    }
}

 

프록시를 생성하는 코드

package hello.proxy.cglib.code;

import hello.proxy.common.service.ConcreteService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;
@Slf4j
public class CglibTest {
    @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());
        log.info("proxyClass={}",proxy.getClass());

        proxy.call();
    }
}

CGLIB 제약

클래스 기반 프록시는 상속을 사용하기 때문에 몇가지 제약이 있다.

 

  • 부모 클래스의 생성자를 체크해야 한다.
  • CGLIB는 자식 클래스를 동적으로 생성하기 때문에 기본 생성자가 필요하다.
  • 클래스에 final 키워드가 붙으면 상속이 불가능하다. CGLIB에서는 예외가 발생한다.
  • 메서드에 final 키워드가 붙으면 해당 메서드를 오버라이딩 할 수 없다.
  • CGLIB에서는 프록시 로직이 동작하지 않는다

+ Recent posts