Skip to content

Latest commit

 

History

History
114 lines (84 loc) · 4.26 KB

CGLIB.md

File metadata and controls

114 lines (84 loc) · 4.26 KB

CGLIB

CGLIB : Code Generator Library

  • CGLIB 은 바이트코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리이다.
  • CGLIB 은 JDK Dynamic Proxy 와 달리 인터페이스 없이, 구체 클래스만 가지고 동적 프록시를 생성할 수 있다.
  • CGLIB는 구체 클래스를 상속(extends)해서 프록시를 만든다.
    • 따라서, 상속에 의한 제약조건이 생긴다.
    • CGLIB는 자식 클래스를 동적으로 생성하기 때문에 기본 생성자가 필요하다.
    • 클래스 혹은 메서드에 final 이 붙으면 안된다.
  • CGLIB 은 외부 라이브러리이지만, 스프링 프레임워크가 스프링 내부 소스 코드에 포함했다. 따라서, 스프링을 사용하는 경우 별도의 라이브러리를 추가할 필요 없이 사용할 수 있다.
  • 성능 차이 분석 표

CGLIB Proxy 는 상속을 통해 Proxy 를 구현하기 때문에 final 클래스인 경우 Proxy 를 생성할 수 없다. kotlin 의 경우에 모든 클래스들이 default 로 final 이기 때문에 AOP 이슈가 생길 수도 있다.

cglib

package org.springframework.cglib.proxy;

import java.lang.reflect.Method;

public interface MethodInterceptor extends Callback {
    /**
     * @params obj : CGLIB 이 적용된 객체
     * @params method : 호출된 메서드
     * @params args : 메서드를 호출하면서 전달된 인수
     * @params proxy : 메서드 호출에 사용
     */
    Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}

Example

Proxy 는 항상 호출할 대상(target) 이 필요하다.

  • ConcreteC
@Slf4j
public class ConcreteC {
    public String call() {
        log.info("C 호출");
        return "C";
    }
}
  • MethodInterceptor 구현체
    • MethodInterceptor 를 구현하여 CGLIB 프록시의 실행 로직을 정의한다.
@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;
    }
}

Method 를 사용해도 되는데, CGLIB 자체에서 내부적으로 최적화 하는 코드가 존재해서 MethodProxy 를 사용하여 호출하는 것이 성능상 이점이 있다고 한다.

  • 테스트
@Test
void cglib() {
    ConcreteC target = new ConcreteC();

    // Enhancer 가 CGLIB Proxy 를 만든다.
    Enhancer enhancer = new Enhancer();
    
    // CGLIB 는 구체 클래스를 상속 받아서 프록시를 생성할 수 있다.
    // 어떤 구체 클래스를 상속 받을지 정한다.
    enhancer.setSuperclass(ConcreteC.class);
    
    // // 프록시에 적용할 실행 로직을 할당한다.
    enhancer.setCallback(new TimeMethodInterceptor(target));
    
    // 프록시 생성
    // ConcreteC$$EnhancerByCGLIB$$860aca8f@2209
    ConcreteC proxy = (ConcreteC) enhancer.create();
    
    log.info("targetClass={}", target.getClass()); // targetClass=class reflection.study.dynamicproxy.code.ConcreteC
    log.info("proxyClass={}", proxy.getClass()); // proxyClass=class reflection.study.dynamicproxy.code.ConcreteC$$EnhancerByCGLIB$$860aca8f

    proxy.call();
}

CGLIB2

References