博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jdk和cglib简单理解
阅读量:4838 次
发布时间:2019-06-11

本文共 4784 字,大约阅读时间需要 15 分钟。

  之前使用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 //直接快速的执行方法..........  }}

  

转载于:https://www.cnblogs.com/onlywujun/p/3524690.html

你可能感兴趣的文章
1019 General Palindromic Number (20 分)
查看>>
关于c语言中指针的一些理解
查看>>
Expm 2_2 查找中项问题
查看>>
启动与关闭hadoop
查看>>
7.2 Move Field(搬移字段)
查看>>
[置顶] C#执行Excel宏模版的方法
查看>>
2015年9月28日JQuery提前预习预热笔记
查看>>
perl 删除过期文件
查看>>
document.write与document.getElementById的区别
查看>>
搜索可用docker镜像
查看>>
python基础知识梳理-----7函数
查看>>
函数极限的定义
查看>>
POJ 3684 Priest John's Busiest Day 2-SAT+输出路径
查看>>
oracle10g、oracle client和plsql devement 三者之间的关系
查看>>
ICDM评选:数据挖掘十大经典算法
查看>>
巧用「打印」功能实现PDF单页提取
查看>>
【转】Mongo初体验
查看>>
Java中synchronized和同步方法
查看>>
C语言学习记录_2019.01.29
查看>>
个人简历
查看>>