cglib动态代理实现原理详细分析

在之前Java代理模式中大致的分析了下代理模式的类型及对每种代理类型简单的举例了下。在上篇JDK动态代理实现原理详细分析中,对其JDK代理的流程做了一个详细的分析。而本文,将介绍另一种动态代理模式:cglib动态代理。阅读完本文,你将对cglib代理模式的运行的流程有一个清晰的认识。

本文的目录如下:

目录

一:cglib动态代理的样例展示

二:cglib生成的代理类的分析

三:cglib运行流程详细的分析(结合源码)

四:小结


一:cglib动态代理的样例展示

public class MyCglib implements MethodInterceptor {

    public Object getInstance(Object object){
        //创建增强类
        Enhancer enhancer = new Enhancer();
        //设置生成的代理类的父类为传入的对象类
        enhancer.setSuperclass(object.getClass());
        //设置方法的拦截回调为当前对象(方法拦截的时候会调用intercept方法)
        enhancer.setCallback(this);
        //返回创建的动态代理对象(Enhancer有静态方法create可以直接调用)
        return enhancer.create();
    }


    /**
     * @param subProxy      生成的代理对象  该类是目标类的子类
     * @param method        原目标方法
     * @param objects       方法的参数
     * @param methodProxy   生成的代理方法
     */
    public Object intercept(Object subProxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里执行的逻辑将在下文详细分析
        return methodProxy.invokeSuper(subProxy,objects);
    }

}

目标类:

public class ZhangSan {
    public String say(){
        return "说中文";
    }
}

测试类:

public class CglibTest {
    public static void main(String[] args) throws IOException {
        ZhangSan instance = (ZhangSan) new MyCglib().getInstance(new ZhangSan());
        instance.say();
    }
    static{
        //用于生成cglib字节码文件,用于下文分析,生成文件的保存路径为:D:\Tools\Study
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Tools\\Study");
    }
}

在测试类中,有一个cglib生成的代理对象instance,代理对象调用了say方法,而最终执行的却是目标类的say方法。要想弄清这个问题,我们看看生成的代理对象instance到底是什么结构。

二:cglib生成的代理类的分析

 通过System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Tools\\Study"),我们可以在对应的目录下生成代理类的class文件。class文件反编译之后的结果为:

public class ZhangSan$$EnhancerByCGLIB$$c4d2de63 extends ZhangSan implements Factory
{
  private boolean CGLIB$BOUND;
  public static Object CGLIB$FACTORY_DATA;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static Object CGLIB$CALLBACK_FILTER;
  
  private static final Method CGLIB$say$0$Method;      //重写的目标方法
  private static final MethodProxy CGLIB$say$0$Proxy;  //生成的代理方法
  
  private static final Object[] CGLIB$emptyArgs;       //方法参数
  
  //父类中的方法都会在这里有一个重写的方法以及代理生成的方法
  private static final Method CGLIB$equals$1$Method;
  private static final MethodProxy CGLIB$equals$1$Proxy;
  
  private static final Method CGLIB$toString$2$Method;
  private static final MethodProxy CGLIB$toString$2$Proxy;
  
  private static final Method CGLIB$hashCode$3$Method;
  private static final MethodProxy CGLIB$hashCode$3$Proxy;
  
  private static final Method CGLIB$clone$4$Method;
  private static final MethodProxy CGLIB$clone$4$Proxy;

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
	
    Class localClass1 = Class.forName("com.sg.spring05.cglib.ZhangSan$$EnhancerByCGLIB$$c4d2de63");
    Class localClass2;
	
    Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    
    CGLIB$equals$1$Method = tmp83_80[0];
    CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    
    Method[] tmp103_83 = tmp83_80;
    CGLIB$toString$2$Method = tmp103_83[1];
    CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    
    Method[] tmp123_103 = tmp103_83;
    CGLIB$hashCode$3$Method = tmp123_103[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
    
    Method[] tmp143_123 = tmp123_103;
    CGLIB$clone$4$Method = tmp143_123[3];
    CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    tmp143_123;
	
    //具体的设置重写的方法以及代理的方法
    Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "say", "()Ljava/lang/String;" }, (localClass2 = Class.forName("com.sg.spring05.cglib.ZhangSan")).getDeclaredMethods());
    CGLIB$say$0$Method = tmp191_188[0];
    CGLIB$say$0$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "say", "CGLIB$say$0");
    tmp191_188;
  }

  //代理生成的方法  直接调用的父类(目标类的方法)
  final String CGLIB$say$0(){
    return super.say();
  }

  //方法重写 测试样例中就是调用的这里的say方法
  public final String say()
  {
	//判断目标类是否有设置回调:enhancer.setCallback(this);
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null){
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
	//设置了方法的回调则调用拦截器方法intercept
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    return super.say();
  }

  .......省略了equals,toString,hashCode,clone方法.........
  

  public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
  {
    String tmp4_1 = paramSignature.toString();
    switch (tmp4_1.hashCode())
    {
    case -1257330122:
      if (tmp4_1.equals("say()Ljava/lang/String;"))
        return CGLIB$say$0$Proxy;
    case -508378822:
    case 1826985398:
    case 1913648695:
    case 1984935277:
    }
  }

  public ZhangSan$$EnhancerByCGLIB$$c4d2de63()
  {
    CGLIB$BIND_CALLBACKS(this);
  }

  public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
  }

  public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
  }

  ..........
  public Object newInstance(Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new c4d2de63();
  }

  public Object newInstance(Callback paramCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new c4d2de63();
  }

  public Object newInstance(Class[] paramArrayOfClass, Object[] paramArrayOfObject, Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    Class[] tmp9_8 = paramArrayOfClass;
    switch (tmp9_8.length)
    {
    case 0:
      tmp9_8;
      break;
    default:
      new c4d2de63();
      throw new IllegalArgumentException("Constructor not found");
    }
    CGLIB$SET_THREAD_CALLBACKS(null);
  }

  public Callback getCallback(int paramInt)
  {
    CGLIB$BIND_CALLBACKS(this);
    switch (paramInt)
    {
    case 0:
      break;
    }
    return null;
  }

  public void setCallback(int paramInt, Callback paramCallback)
  {
    switch (paramInt)
    {
    case 0:
      this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
      break;
    }
  }

  public Callback[] getCallbacks()
  {
    CGLIB$BIND_CALLBACKS(this);
    return new Callback[] { this.CGLIB$CALLBACK_0 };
  }

  public void setCallbacks(Callback[] paramArrayOfCallback)
  {
    this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
  }

  static
  {
    CGLIB$STATICHOOK1();
  }
}

在生成的代理类(ZhangSan$$EnhancerByCGLIB$$c4d2de63)中我们可以看到它继承与目标类(ZhangSan)。在生成的代理类中,对于父类中每一个能够继承重写的方法,动态代理类都会生成两个相应的方法。一个是直接重写父类的方法,一个是生成的对应的动态代理的方法。在调用时,会直接先调用重写的方法。

三:cglib运行流程详细的分析(结合源码)

分析cglib动态代理的运行流程,我们从测试类中的调用方法开始入手:

ZhangSan instance = (ZhangSan) new MyCglib().getInstance(new ZhangSan());
instance.say();

即instance.say()为入口切入。这里的instance为生成的代理类对象,调用say方法的时候,实际上是调用的为代理类重写的父类的方法。即:

  public final String say()
  {
    //判断目标类是否有设置回调:enhancer.setCallback(this);
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null){
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    //设置了方法的回调则调用拦截器方法intercept
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    return super.say();
  }

首先判断是否目标类对象是否设置了回调的方法,即设置了enhancer.setCallback(this);如果不为空,则执行:

(String)tmp17_14.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);

直接执行生成代理类的intercept方法,参数为:this(当前代理对象),CGLIB$say$0$Method(目标类中的方法),CGLIB$emptyArgs(方法参数,这里为空),CGLIB$say$0$Proxy(代理类生成的代理方法)。即这里会调用到样例中MyCglib.intercept方法。代码如下:

 public Object intercept(Object subProxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(subProxy,objects);
    }

我们接下来看看在invokeSuper中做了一些什么。

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            //初始化生成一个FastClassInfo
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //执行相应的FastClass的方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

生成一个FastClassInfo和执行其对应的方法。那么FastClassInfo又是什么?其源码如下:

private static class FastClassInfo {
        FastClass f1;   //目标类
        FastClass f2;   //代理类
        int i1;     //目标类要执行方法的下标
        int i2;     //代理类要执行方法的下标

        private FastClassInfo() {
        }
}

其中f1为对应的目标类,f2为生成的代理类,i1为目标类(f1)中实际要执行的方法,i2为代理类(f2)中实际要执行的方法。这样的好处是,类与方法直接有一个下标的映射关系,通过对应的下标,可以直接调用方法而不是通过反射的方法。而反射则会更加的耗时。对应详细的FastClass机制,这里不再赘述。明白了这边,我们再回到代码中:

 MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, arg);

首先生成一个FastClassInfo对象fci,保存了目标类,代理类,目标方法下标,代理类执行的下标。而fci.f2.invoke(fci.i2, obj, arg);调用的的为代理类的对应的目标方法(f2).即又调用回了目标类的方法,即:

final String CGLIB$say$0(){
    return super.say();
  }

而这里,才是真正额调用调用到了父类(目标类)中对应的方法。至此,整个的调用流程完毕。

四:小结

cglib动态代理小结:

(1)首先生成代理对象。【创建增强类enhancer,设置代理类的父类,设置回调拦截方法,返回创建的代理对象】

(2)调用代理类中的方法。【这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用intercept方法】

(3)调用intercept,方法中会对调用代理方法中的invokeSuper方法。而在invokeSuper中维护了一个FastClassInfo类,其包含四个属性字段,分别为FastClass f1(目标类);FastClass f2 (代理类); int i1(目标类要执行方法的下标); int i2(代理类要执行方法的下标); invokeSuper中会调用的为代理类中的对应方法(代理类继承于父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法)

(4)调用代理类中的代理方法,代理方法中通过super.method来实际真正的调用要执行的方法。

 

 

 

你可能感兴趣的:(JAVA那些事,设计模式)