CGLIB 动态代理 原理分析

本文介绍CGLIB动态代理的机制,JDK动态代理机制可以参考另一篇文章JDK 动态代理 原理分析

直接上例子,需要的依赖包

先来个实现类

package proxy;

public class User {

	public void methodPublic1() {
		System.out.println("methodPublic1");
	}

	public void methodPublic2(String a) {
		System.out.println(a+"========methodPublic2");
	}

	public void defaultMethod1(int b) {
		System.out.println(b+"========defaultMethod1");
	}

	public void defaultMethod2() {
		System.out.println("defaultMethod2");
	}

}

拦截类

package proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class UserMethodInterceptor implements MethodInterceptor {

	@Override
	public Object intercept(Object obj, Method method, Object[] arg,
			MethodProxy proxy) throws Throwable {
		System.out.println("before:"+method.getName());
		Object object = proxy.invokeSuper(obj, arg);
		System.out.println("after:"+method.getName());
		return object;
	}

}

测试类

public class Testcglib {

	public static void main(String[] args) {
                //该设置用于输出cglib动态代理产生的类
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglibProxyClass");  
		Enhancer enhancer = new Enhancer();
		//继承被代理类
		enhancer.setSuperclass(User.class);
		//设置回调
		enhancer.setCallback(new UserMethodInterceptor());
		//生成代理类对象
		User user = (User)enhancer.create();
		//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
		user.methodPublic1();
		user.methodPublic2("22222222");
		user.defaultMethod1(111);
		user.defaultMethod2();

	}

}

结果

整个过程相比jdk动态代理简单了一些。不同的是JDK动态代理强制要求实现类必须最少实现一个接口,但是CGLIB则可以要求实现类不用实现任何接口。CGLIB动态生成的代理类会继承我们的业务类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。CGLIB是高效的代码生成包,底层依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。

代理类对象是由Enhancer类创建的。Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展

创建代理对象的几个步骤:

1、生成代理类的二进制字节码文件

2、加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 )

3、通过反射机制获得实例构造,并创建代理类对象

在测试类中加了System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglibProxyClass"); 这段代码,我们可以在d盘cglibProxyClass文件夹下找到cglib生成的class文件

package proxy;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class User$$EnhancerByCGLIB$$23ef74e2 extends User
  implements Factory
{
  private boolean CGLIB$BOUND;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static final Method CGLIB$defaultMethod1$0$Method;
  private static final MethodProxy CGLIB$defaultMethod1$0$Proxy;
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$defaultMethod2$1$Method;
  private static final MethodProxy CGLIB$defaultMethod2$1$Proxy;
  private static final Method CGLIB$methodPublic1$2$Method;
  private static final MethodProxy CGLIB$methodPublic1$2$Proxy;
  private static final Method CGLIB$methodPublic2$3$Method;
  private static final MethodProxy CGLIB$methodPublic2$3$Proxy;
  private static final Method CGLIB$hashCode$4$Method;
  private static final MethodProxy CGLIB$hashCode$4$Proxy;
  private static final Method CGLIB$equals$5$Method;
  private static final MethodProxy CGLIB$equals$5$Proxy;
  private static final Method CGLIB$clone$6$Method;
  private static final MethodProxy CGLIB$clone$6$Proxy;
  private static final Method CGLIB$toString$7$Method;
  private static final MethodProxy CGLIB$toString$7$Proxy;
  private static final Method CGLIB$finalize$8$Method;
  private static final MethodProxy CGLIB$finalize$8$Proxy;

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("proxy.User$$EnhancerByCGLIB$$23ef74e2");
    Class localClass2;
    Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "hashCode", "()I", "equals", "(Ljava/lang/Object;)Z", "clone", "()Ljava/lang/Object;", "toString", "()Ljava/lang/String;", "finalize", "()V" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$hashCode$4$Method = tmp95_92[0];
    CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
    Method[] tmp115_95 = tmp95_92;
    CGLIB$equals$5$Method = tmp115_95[1];
    CGLIB$equals$5$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$5");
    Method[] tmp135_115 = tmp115_95;
    CGLIB$clone$6$Method = tmp135_115[2];
    CGLIB$clone$6$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
    Method[] tmp155_135 = tmp135_115;
    CGLIB$toString$7$Method = tmp155_135[3];
    CGLIB$toString$7$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$7");
    Method[] tmp175_155 = tmp155_135;
    CGLIB$finalize$8$Method = tmp175_155[4];
    CGLIB$finalize$8$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$8");
    tmp175_155;
    Method[] tmp256_253 = ReflectUtils.findMethods(new String[] { "defaultMethod1", "(I)V", "defaultMethod2", "()V", "methodPublic1", "()V", "methodPublic2", "(Ljava/lang/String;)V" }, (localClass2 = Class.forName("proxy.User")).getDeclaredMethods());
    CGLIB$defaultMethod1$0$Method = tmp256_253[0];
    CGLIB$defaultMethod1$0$Proxy = MethodProxy.create(localClass2, localClass1, "(I)V", "defaultMethod1", "CGLIB$defaultMethod1$0");
    Method[] tmp276_256 = tmp256_253;
    CGLIB$defaultMethod2$1$Method = tmp276_256[1];
    CGLIB$defaultMethod2$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "defaultMethod2", "CGLIB$defaultMethod2$1");
    Method[] tmp296_276 = tmp276_256;
    CGLIB$methodPublic1$2$Method = tmp296_276[2];
    CGLIB$methodPublic1$2$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "methodPublic1", "CGLIB$methodPublic1$2");
    Method[] tmp316_296 = tmp296_276;
    CGLIB$methodPublic2$3$Method = tmp316_296[3];
    CGLIB$methodPublic2$3$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)V", "methodPublic2", "CGLIB$methodPublic2$3");
    tmp316_296;
    return;
  }

  final void CGLIB$defaultMethod1$0(int paramInt)
  {
    super.defaultMethod1(paramInt);
  }

  public final void defaultMethod1(int paramInt)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.defaultMethod1(paramInt);
  }

  final void CGLIB$defaultMethod2$1()
  {
    super.defaultMethod2();
  }

  public final void defaultMethod2()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.defaultMethod2();
  }

  final void CGLIB$methodPublic1$2()
  {
    super.methodPublic1();
  }

  public final void methodPublic1()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.methodPublic1();
  }

  final void CGLIB$methodPublic2$3(String paramString)
  {
    super.methodPublic2(paramString);
  }

  public final void methodPublic2(String paramString)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.methodPublic2(paramString);
  }

  final int CGLIB$hashCode$4()
  {
    return super.hashCode();
  }

  public final int hashCode()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.hashCode();
  }

  final boolean CGLIB$equals$5(Object paramObject)
  {
    return super.equals(paramObject);
  }

  public final boolean equals(Object paramObject)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$5$Method, new Object[] { paramObject }, CGLIB$equals$5$Proxy);
      tmp41_36;
      return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
    }
    return super.equals(paramObject);
  }

  final Object CGLIB$clone$6()
    throws CloneNotSupportedException
  {
    return super.clone();
  }

  protected final Object clone()
    throws CloneNotSupportedException
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
      return tmp17_14.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy);
    return super.clone();
  }

  final String CGLIB$toString$7()
  {
    return super.toString();
  }

  public final String toString()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$toString$7$Method, CGLIB$emptyArgs, CGLIB$toString$7$Proxy);
    return super.toString();
  }

  final void CGLIB$finalize$8()
    throws Throwable
  {
    super.finalize();
  }

  protected final void finalize()
    throws Throwable
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.finalize();
  }

  public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
  {
    String tmp4_1 = paramSignature.toString();
    switch (tmp4_1.hashCode())
    {
    case -1574182249:
      if (tmp4_1.equals("finalize()V"))
        return CGLIB$finalize$8$Proxy;
    case -508378822:
    case -411343995:
    case 132343965:
    case 604503886:
    case 987425847:
    case 1826985398:
    case 1913648695:
    case 1984935277:
    }
  }

  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;
  }

  private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
  {
    // Byte code:
    //   0: aload_0
    //   1: checkcast 2	proxy/User$$EnhancerByCGLIB$$23ef74e2
    //   4: astore_1
    //   5: aload_1
    //   6: getfield 248	proxy/User$$EnhancerByCGLIB$$23ef74e2:CGLIB$BOUND	Z
    //   9: ifne +43 -> 52
    //   12: aload_1
    //   13: iconst_1
    //   14: putfield 248	proxy/User$$EnhancerByCGLIB$$23ef74e2:CGLIB$BOUND	Z
    //   17: getstatic 24	proxy/User$$EnhancerByCGLIB$$23ef74e2:CGLIB$THREAD_CALLBACKS	Ljava/lang/ThreadLocal;
    //   20: invokevirtual 251	java/lang/ThreadLocal:get	()Ljava/lang/Object;
    //   23: dup
    //   24: ifnonnull +15 -> 39
    //   27: pop
    //   28: getstatic 246	proxy/User$$EnhancerByCGLIB$$23ef74e2:CGLIB$STATIC_CALLBACKS	[Lnet/sf/cglib/proxy/Callback;
    //   31: dup
    //   32: ifnonnull +7 -> 39
    //   35: pop
    //   36: goto +16 -> 52
    //   39: checkcast 252	[Lnet/sf/cglib/proxy/Callback;
    //   42: aload_1
    //   43: swap
    //   44: iconst_0
    //   45: aaload
    //   46: checkcast 53	net/sf/cglib/proxy/MethodInterceptor
    //   49: putfield 37	proxy/User$$EnhancerByCGLIB$$23ef74e2:CGLIB$CALLBACK_0	Lnet/sf/cglib/proxy/MethodInterceptor;
    //   52: return
  }

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

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

  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 23ef74e2();
      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();
  }
}

我们看下动态生成的代理类的关键代码

final void CGLIB$methodPublic1$2()
  {
    super.methodPublic1();
  }

  public final void methodPublic1()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null)
      return;
    super.methodPublic1();
  }

首先呢,动态代理类是继承了我们的业务类,所以如果业务类是final修饰的那么则不能被代理,同理final修饰的方法也是不能被代理的

动态代理类根据我们业务类的每一个方法都生成了2个代理方法,第一个代理方法直接调用父类的方法,也就是我们业务类的方法,第二个方法也就是代理类真正调用的方法是经过封装的,他会去判断是否实现了MethodInterceptor 的intercept方法,如果实现了则会调用intercept方法。这个MethodInterceptor 就是我们在enhancer.setCallback(new UserMethodInterceptor());这边设置进去的。

从我们定义的拦截器的intercept方法中可以看到Object object = proxy.invokeSuper(obj, arg);是真正调用的方法,我们跟进去

/**
     * Invoke the original (super) method on the specified object.
     * @param obj the enhanced object, must be the object passed as the first
     * argument to the MethodInterceptor
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

首先会调用init方法,这个init方法实现什么功能呢,继续跟进去

private void init()
    {
        /* 
         * Using a volatile invariant allows us to initialize the FastClass and
         * method index pairs atomically.
         * 
         * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this 
         * code could allow fastClassInfo to be instantiated more than once, which
         * appears to be benign.
         */
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;

                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

它的功能就是初始化我们代理类的一些信息,这里的f1是我们的业务类的信息,f2是代理类的信息。i1和i2代表动态代理类为 我们的业务方法生成的2个代理方法的对象信息数组下标。

初始化完成后调用fci.f2.invoke(fci.i2, obj, args);方法。这个方法很像反射方法,其实它并不是通过反射找到指定的方法的,而是在创建代理类的时候为其中的方法建立hash索引,这样调用的时候通过索引调用提高了效率。

编辑于 2018-03-31