jdk和cglib簡單理解


  之前使用cglib的時候不需要將classLoader作為參數傳入,但動態代理卻要,帶着這個疑惑進入這個方法:

  Proxy.newProxyInstance(classLoader, interfaces, InvocationHandler)

  要在classLoader里去找interfaces,如果也加載進來了才能繼續執行,並且用ProxyGenerator動態生成了一個代理類的字節碼文件(使用了緩存技術,只需要生成一次),然后用classLoader將這個字節碼文件加載進來。這就是classLoader的作用。

  可以這樣看生成的字節碼類。

  加入執行參數:

  System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles""true"

  生成的字節碼文件就會保留下來,然后編譯出來如下:

package demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IA {
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean) this.h.invoke(this, m1, 
          new Object[] { paramObject })).booleanValue();
    } catch (RuntimeException localRuntimeException) {
      throw localRuntimeException;
    } catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int b(String paramString) {
    try {
      return ((Integer) this.h.invoke(this, m4,
           new Object[] { paramString })).intValue();
    } catch (RuntimeException localRuntimeException) {
      throw localRuntimeException;
    } catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void a() {
    try {
      this.h.invoke(this, m3, null);
      return;
    } catch (RuntimeException localRuntimeException) {
      throw localRuntimeException;
    } catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode() {
    try {
      return ((Integer) this.h.invoke(this, m0, null)).intValue();
    } catch (RuntimeException localRuntimeException) {
      throw localRuntimeException;
    } catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString() {
    try {
      return (String) this.h.invoke(this, m2, null);
    } catch (RuntimeException localRuntimeException) {
      throw localRuntimeException;
    } catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals",
          new Class[] { Class.forName("java.lang.Object") });
      m4 = Class.forName("demo.IA").getMethod("b", 
          new Class[] { Class.forName("java.lang.String") });
      m3 = Class.forName("demo.IA").getMethod("a", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    } catch (NoSuchMethodException localNoSuchMethodException) {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    } catch (ClassNotFoundException localClassNotFoundException) {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

可以發現所有接口方法的實現都委托給InvocationHandler的invoke方法了,這也就是實現代理模式的地方了。

 

--------------------------------------------------------------------------

cglib不需要傳入ClassLoader,代碼里會自己去找上下文的ClassLoader,這種設計使少傳一個ClassLoader這種很少見的參數對初學者來說用起來要簡單點。

可以設置System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "字節碼文件保存位置",把cglib生成的動態字節碼保存下來。

單間分析下生成的字節碼

動態生成的繼承類會改寫我們使用的父類的所有方法,攔截下來交給設置的MethodInterceptor去執行。

  public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy)

第一個參數obj就是動態生成的子類。第二個參數是原始類的方法。

  我們一般使用proxy.invokeSuper(obj,args)方法。這個很好理解,就是執行原始類的方法。還有一個方法proxy.invoke(obj,args),這是執行生成子類的方法。如果傳入的obj就是子類的話,會發生內存溢出,因為子類的方法不挺地進入intercept方法,而這個方法又去調用子類的方法,兩個方法直接循環調用了。

  我們來看看MethodProxy,原始類里每一個方法都會在動態的子類里有一個對應的MethodProxy,而一個MethodProxy又對應了兩個動態生成的FastClass類,一個是對應原始方法,一個對應新生成的子類,MethodProxy.invokeSuper就是交給對應原始方法那個FastClass,MethodProxy.invoke交給另一個。

  這2個額外生成的類作用在於當我們調用一個方法時,不通過反射來調用,而是通過類似於數組下標的方式來定位方法,直接進行類方法的執行。

  FastClass生成的代碼類似這樣的

public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject)
    throws InvocationTargetException
  {
    // Byte code:
       0: aload_2 
       1: checkcast 159	net/sf/cglib/mytest/A$$EnhancerByCGLIB$$f84d7df
       4: iload_1   //paramInt參數入棧
       5: tableswitch	default:+403 -> 408, 0:+131->136..... //通過paramInt也就相當於數組小標志,定位到方法執行的代碼段
.....
.....
       148: aload_3
       149: iconst_0
       150: aaload
       151: invokevirtual 166	net/sf/cglib/mytest/A$$EnhancerByCGLIB$$f84d7df:equals	(Ljava/lang/Object;)Z //直接快速的執行方法
.....
.....
  }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM