動的プロキシCGLIBの動的エージェント

CGLIBのダイナミックプロキシ

欠陥が存在し、それがインターフェイスメソッドの唯一の薬剤である、プロキシクラス自身と親クラスのメソッドは、エージェントではないことがあることを、我々は詳細、自分のJDK APIの動的なエージェントが提供する前に、それはまた、ショー。

CGLIBは、この問題の学生、高エネルギー、唯一のインターフェイスメソッドのために代理店の質問を行うことができ、動的プロキシのJDKのバージョンへの完璧なソリューションに基づいて基本的なコード生成フレームワークのASMの枠組みを解決することです。

例でみましょう初見。まず、人の定義は、法のsayHelloを定義して指定されたクラスで、父を話すし、実行することができます。

public class Father{
  public void sayHello(){
    System.out.println("hi somebody....");
  }
}

public interface Person{
  void speak();
  void run();
}

ここでは、それ自体がクラス父から継承し、当社のプロキシクラスは、ある、と人のインターフェイスを実現します。

public class Student extends Father implements Person{
  // student 类自己实现的方法
  public void study(){
    System.out.println("i can study....");
  }
  
  @Override
  public void speak(){
    System.out.println("i am a student, i can speak....");
  }
  
  @Override
  public void run(){
    System.out.println("i am a student, i can run....");
  }
}

CGLIBインターセプター、JDKの動的プロキシのInvocationHandlerのような迎撃ビットを定義します。

public class MyMethodInterceptor implmenets MethodInterceptor{
  public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy){
    System.out.println("Before:" + method);
    Object object = proxy.invokeSuper(obj,arg);
    System.out.println("After:" + method);
    return object;
  }
}

CGLIBプロキシクラスを作成すると、サブクラスは、プロキシクラスの型に強くすることができます。

public class Test{
  public static void main(String[] args){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Student.class);
    enhancer.setCallback(new MyMethodInterceptor());
    Student stu = (Student)enhancer.create();
    stu.sayHello();
    stu.run();
    stu.study();
  }
}

すべてのメソッドは薬することができます。

原則

我々はまだ逆コンパイル彼の作品を見てください。

  1. 学生は、プロキシクラス生成されたプロキシクラスがプロキシクラスの直接の後継者、そのサブクラスです。不具合を解決するためにJDKエージェント。
  2. 工場インターフェースは、当社のインターセプタでコールバックを設定および取得するために使用されます。

プロキシクラスは、親クラスのインタフェースと親クラス、親クラス及び親クラスを含む方法を反映して、すべての方法により取得された親クラス・プログラム。

親クラスがプロキシクラスのすべてのメソッドである書き換えます。インターセプターにパラメータとして渡され、現在のメソッドのシグネチャは、インターセプタはまた、コールバックと呼びます。

このような観点から、JDKの動的プロキシと動的プロキシCGLIBは類似している、コールバックに依存する必要、プロセッサとしてCGLIB、JDKインターセプターと呼ばれます。

プロキシクラスのノートへのもう一つのポイントは、すべてのメソッドには2つのバージョン、インターセプタせずに、以前は他の対応方法を書き換える方法があります。これは、結果CGLIB FastClassメカニズムである、FastClass私たちは滞在が紹介されて。

プロキシクラスのすべてのメソッドは、インターセプタの処理について説明するので、私たちは、インターセプタの内部のパラメータの意味を知ることができるようになります。

  • OBJ:代表プロキシクラスのインスタンスオブジェクト。
  • 方法:現在のメソッド呼び出しを指します。
  • 引数:メソッドの呼び出しのパラメータ。
  • 它代表着当前方法的引用,基于 FastClass 机制。

我们知道 Method 是基于反射来调用的,但是反射的效率总是要低于直接调用的,而 MethodProxy 基于 FastClass 机制对方法直接下标索引,并通过索引直接定位和调用方法,是一点性能上的提升。

FastClass

在拦截器中我们使用的是 MethodProxy 调用的方法,我们说过 MethodProxy 是基于 FastClass 机制对方法的直接下标索引。下面我们通过它来了解一下 FastClass。

MethodProxy 的源码:

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    MethodProxy proxy = new MethodProxy();
    proxy.sig1 = new Signature(name1, desc);
    proxy.sig2 = new Signature(name2, desc);
    proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
    return proxy;
}
  1. desc 代表是一个方法的描述符。
  2. c1 代表的是这个方法所属的类,就是被代理类。
  3. c2 代表的值往往是我们生成的代理类。
  4. name1 是委托方法中的方法名。
  5. name2 是代理类中该方法的方法名。

举个例子:

var1 = Class.forName("Main.Student");
var0 = Class.forName("Main.Student$$EnhancerByCGLIB$$56e20d66");
MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$3");

var1 是我们的被代理类,var0 是我们的代理类,[()V] 是 sayHello 方法的方法签名,CGLIB$sayHello$3 是 sayHello 方法在代理类中的方法名。

有了这几个参数,MethodProxy 就可以出实话一个 FastClassInfo。

private static class FastClassInfo {
    FastClass f1;
    FastClass f2;
    int i1;
    int i2;
    private FastClassInfo() {
    }
}

FastClass 内部包含一个 class 对象,并且会对其中所有方法进行索引标记,于是外部对于任意方法的调用只需要提供一个索引值,FastClass 就能快速定位到具体方法。

这里 f1 包装着被代理类,f2 包装着代理类。i1 是当前方法在 f1 中的索引值,i2 是当前方法在 f2 中的索引值。所以基于 FastClass 的调用很简单,invoke 方法指定一个索引即可,而不需要传统的反射方式了。

缺点

CGLIB 虽然解决了 JDK 动态代理的致命问题,它可以代理父类以及自身以及父接口中的方法,但是他也不是所有的方法都能代理。

CGLIB 需要继承被代理类,如果被代理类被 final 修饰,那就意味着,CGLIB 代理不了。或者说 final 修饰的方法也代理不了,因为 CGLIB 生成的代理类需要重写被代理类的方法,而被 final 修饰的方法是不与许被重写的。

おすすめ

転載: www.cnblogs.com/paulwang92115/p/12173652.html