CGLIB

3609 ワード

CGLIB


CGIBは、バイトコードを処理することによってクラスを動的に生成する技術を提供するライブラリである.
CGIBを使用すると、インタフェースがなくても、特定のクラスを使用して動的エージェントを作成するしかありません.
CGLIBは元々外部ライブラリであり、スプリングフレームがスプリング内部ソースコードに含まれているため、スプリングを使用する場合は、追加の外部ライブラリを追加する必要はありません.
cf)を参考にして、スプリングのProxyFactoryはCGIBを簡単に使用できるので、大まかな概念をつかむだけでいいです.
JDKダイナミックエージェントが実行ロジックにInvocationHandlerを提供するように,CGIBはメソッドインタフェースを提供する.
public interface MethodInterceptor extends Callback {
    Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}

サンプルコード

public interface ServiceInterface {

    void save();

    void find();
}

@Slf4j
public class ServiceImpl implements ServiceInterface{
    @Override
    public void save() {
        log.info("save 호출");
    }

    @Override
    public void find() {
        log.info("find 호출");
    }
}

@Slf4j
public class ConcreteService {

    public void call() {
        log.info("ConcreteService 호출");   
    }
}

@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 실행");
        final long startTime = System.currentTimeMillis();

        final Object result = methodProxy.invoke(target, args);
        
        final long endTime = System.currentTimeMillis();
        final long resultTime = endTime - startTime;
        log.info("TimeProxy 종료 resultTime = {}", resultTime);

        return result;
    }
}
TimeMethodInterceptorは、MethodInterceptorインタフェースを実装することによって、CGIBエージェントの実行ロジックを定義する.
これは、JDKダイナミックエージェントを記述する場合の例とほぼ同じです.
Object target:エージェントが呼び出す実際のターゲット
proxy.invoke(target,args):実際のターゲットを動的に呼び出します.
参考までに、メソッドは使用できますが、CGIBはパフォーマンス上メソッドエージェントの使用を推奨します.
@Slf4j
public class CglibTest {

    @Test
    void cglibTest() {
        final ConcreteService target = new ConcreteService();

        final Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ConcreteService.class);
        enhancer.setCallback(new TimeMethodInterceptor(target));
        final ConcreteService proxy = (ConcreteService) enhancer.create();

        log.info("targetClass = {}", target.getClass());
        log.info("proxyClass = {}", proxy.getClass());
        
        proxy.call();
    }
}

実行結果


targetClass = class hello.proxy.common.service.ConcreteService
proxyClass = class hello.proxy.common.service.ConcreteService $$ EnhancerByCGLIB$$25d6b0e3
TimeProxyの実行
ConcreteServiceの呼び出し
TimeProxy終了resultTime=24
Enhanced:CGIBはEnhancedを使用してプロキシを作成します.
enhancer.setSuperclass:CGIBは、特定のクラスを継承することでエージェントを作成できます.継承するクラスを指定します.
enhancer.コールバックの設定(New Time MethodInterceptor、ターゲット):エージェントに適用される実行ロジックを割り当てます.
enhancer.create():エージェントを作成します.enhancer.setSuperClassで指定したクラスを継承してプロキシを作成します.
JDK動的エージェントはインタフェースを実装することによってエージェントを作成する.
CGIBは、特定のクラスを継承(拡張)することによってプロキシを作成します.

整理する



CGIB制約

  • クラスベースのエージェントは継承を使用するため、いくつかの制限があります.
  • 親の作成者を確認する必要があります->CGIBはサブクラスを動的に作成するため、デフォルトジェネレータが必要です.
  • クラスにfinalキーを追加すると継承できません->CGIBで異常が発生しました.
  • メソッドにfinalキーを追加すると、メソッドを上書きできません.->CGIBではエージェントロジックは機能しません.