CGLIB动态代理

CGLIB是一个强大的代码生成库,常用于AOP框架和Hibernate。它使用ASM操作字节码,允许对无接口类进行代理,弥补了Java动态代理的局限。CGLIB动态代理过程包括创建Enhancer实例、设置目标类、设置回调对象和生成代理类。通过代理类分析,发现CGLIB使用FastClass机制提高调用效率,避免了反射的低效。在实际使用中,可能遇到如NoClassDefFoundError等常见问题,解决这些问题需要正确引入依赖和版本管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github。
CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

CGLIB组织结构

image
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。

CGLIB动态代理示例

要使用CGLIB动态代理,肯定要引入cglib-x.x.x.jar,而CGLIB底层用ASM来操作字节码,因此也要引入asm-x.x.x.jar。(这里用cglib-2.2.2.jar和asm-3.1.jar)

maven依赖坐标为(maven会引入cglib的传递依赖)

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

被代理类:

public class HelloService {

	public String hello(String name) {
		return "hello," + name;
	}

	public String hi(String msg) {
		return "hi,"+msg;
	}

}

方法拦截器:

public class HelloServiceInterceptor implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class<?> clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		System.out.println("动态代理预处理...");
		System.out.println("被拦截方法:" + arg1.getName());
		Object result = arg3.invokeSuper(arg0, arg2);
		System.out.println(result);
		System.out.println("动态代理后处理...");
		return result;
	}

}

测试:

public class CglibDynamicProxy {

	public static void main(String[] args) {
		HelloService proxy = (HelloService)(new HelloServiceInterceptor().getProxy(HelloService.class));
		proxy.hello("张三");
		proxy.hi("cglib动态代理");
	}

}

输出:

动态代理预处理...
被拦截方法:hello
hello,张三
动态代理后处理...
动态代理预处理...
被拦截方法:hi
hi,cglib动态代理
动态代理后处理...

JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。

分析

与JDK动态代理相比,CGLIB可以实现对一般类的代理而无需实现接口。在上例中通过下列步骤来生成目标类Target的代理类:

  1. 创建Enhancer实例;
  2. 通过setSuperclass方法来设置目标类;
  3. 通过setCallback方法来设置拦截对象;
  4. create方法生成Target的代理类,并返回代理类的实例。

Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。

代理类分析

CGLIB动态代理默认不生成代理相关Class文件,可以设置参数
-Dcglib.debugLocation=H:\EclipseWorkspace\proxy-test\bin\cglib

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “H:\EclipseWorkspace\proxy-test\bin\cglib”);
来生成代理类Class文件。

这里稍微提一下代理类生成的调用链:

  1. Enhancer : create()
  2. Enhancer : createHelper()
  3. AbstractClassGenerator : create(Object key)
  4. DefaultGeneratorStrategy : generate(ClassGenerator cg)
  5. Enhancer : generateClass(ClassVisitor v)

最终Enhancer类generateClass(ClassVisitor v)方法生成代理类,至于生成代理类Class文件是在DefaultGeneratorStrategy类的generate(ClassGenerator cg)方法:

return transform(cw.toByteArray());

cw.toByteArray()调用的是DebuggingClassWriter类的toByteArray()方法:

public byte[] toByteArray() {
    
  return (byte[]) java.security.AccessController.doPrivileged(
    new java.security.PrivilegedAction() {
        public Object run() {
            
            
            byte[] b = DebuggingClassWriter.super.toByteArray();
            if (debugLocation != null) {
                String dirs = className.replace('.', File.separatorChar);
                try {
                    new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
                    
                    File file = new File(new File(debugLocation), dirs + ".class");
                    OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
                    try {
                        out.write(b);
                    } finally {
                        out.close();
                    }
......

上面方法中可以看到当debugLocation != null时将Class字节数组b写到debugLocation目录下,而这里的debugLocation在该类静态代码块中初始化:

public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";

static {
    debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
    if (debugLocation != null) {
        System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
        try {
            Class.forName("org.objectweb.asm.util.TraceClassVisitor");
            traceEnabled = true;
        } catch (Throwable ignore) {
        }
    }
}

因此我们可以设置参数cglib.debugLocation来控制代理类生成路径。

反编译代理类HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBdf0ac471

package cglib;

import cglib.HelloService;
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 HelloService$$EnhancerByCGLIB$$df0ac471 extends HelloService 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$hello$0$Method;
   private static final MethodProxy CGLIB$hello$0$Proxy;
   private static final Object[] CGLIB$emptyArgs;
   private static final Method CGLIB$hi$1$Method;
   private static final MethodProxy CGLIB$hi$1$Proxy;
   private static final Method CGLIB$finalize$2$Method;
   private static final MethodProxy CGLIB$finalize$2$Proxy;
   private static final Method CGLIB$equals$3$Method;
   private static final MethodProxy CGLIB$equals$3$Proxy;
   private static final Method CGLIB$toString$4$Method;
   private static final MethodProxy CGLIB$toString$4$Proxy;
   private static final Method CGLIB$hashCode$5$Method;
   private static final MethodProxy CGLIB$hashCode$5$Proxy;
   private static final Method CGLIB$clone$6$Method;
   private static final MethodProxy CGLIB$clone$6$Proxy;


   static void CGLIB$STATICHOOK1() {
      CGLIB$THREAD_CALLBACKS = new ThreadLocal();
      CGLIB$emptyArgs = new Object[0];
      Class var0 = Class.forName("cglib.HelloService$$EnhancerByCGLIB$$df0ac471");
      Class var1;
      Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
      CGLIB$finalize$2$Method = var10000[0];
      CGLIB$finalize$2$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$2");
      CGLIB$equals$3$Method = var10000[1];
      CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
      CGLIB$toString$4$Method = var10000[2];
      CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
      CGLIB$hashCode$5$Method = var10000[3];
      CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
      CGLIB$clone$6$Method = var10000[4];
      CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
      var10000 = ReflectUtils.findMethods(new String[]{"hello", "(Ljava/lang/String;)Ljava/lang/String;", "hi", "(Ljava/lang/String;)Ljava/lang/String;"}, (var1 = Class.forName("cglib.HelloService")).getDeclaredMethods());
      CGLIB$hello$0$Method = var10000[0];
      CGLIB$hello$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "hello", "CGLIB$hello$0");
      CGLIB$hi$1$Method = var10000[1];
      CGLIB$hi$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "hi", "CGLIB$hi$1");
   }

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

   public final String hello(String var1) {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      return var10000 != null?(String)var10000.intercept(this, CGLIB$hello$0$Method, new Object[]{var1}, CGLIB$hello$0$Proxy):super.hello(var1);
   }

   final String CGLIB$hi$1(String var1) {
      return super.hi(var1);
   }

   public final String hi(String var1) {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      return var10000 != null?(String)var10000.intercept(this, CGLIB$hi$1$Method, new Object[]{var1}, CGLIB$hi$1$Proxy):super.hi(var1);
   }

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

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

      if(var10000 != null) {
         var10000.intercept(this, CGLIB$finalize$2$Method, CGLIB$emptyArgs, CGLIB$finalize$2$Proxy);
      } else {
         super.finalize();
      }
   }

   final boolean CGLIB$equals$3(Object var1) {
      return super.equals(var1);
   }

   public final boolean equals(Object var1) {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      if(var10000 != null) {
         Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
         return var2 == null?false:((Boolean)var2).booleanValue();
      } else {
         return super.equals(var1);
      }
   }

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

   public final String toString() {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      return var10000 != null?(String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy):super.toString();
   }

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

   public final int hashCode() {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      if(var10000 != null) {
         Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
         return var1 == null?0:((Number)var1).intValue();
      } else {
         return super.hashCode();
      }
   }

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

   protected final Object clone() throws CloneNotSupportedException {
      MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
      if(this.CGLIB$CALLBACK_0 == null) {
         CGLIB$BIND_CALLBACKS(this);
         var10000 = this.CGLIB$CALLBACK_0;
      }

      return var10000 != null?var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy):super.clone();
   }

   public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
      String var10000 = var0.toString();
      switch(var10000.hashCode()) {
      case -1574182249:
         if(var10000.equals("finalize()V")) {
            return CGLIB$finalize$2$Proxy;
         }
         break;
      case -1090657086:
         if(var10000.equals("hi(Ljava/lang/String;)Ljava/lang/String;")) {
            return CGLIB$hi$1$Proxy;
         }
         break;
      case -508378822:
         if(var10000.equals("clone()Ljava/lang/Object;")) {
            return CGLIB$clone$6$Proxy;
         }
         break;
      case 848333779:
         if(var10000.equals("hello(Ljava/lang/String;)Ljava/lang/String;")) {
            return CGLIB$hello$0$Proxy;
         }
         break;
      case 1826985398:
         if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
            return CGLIB$equals$3$Proxy;
         }
         break;
      case 1913648695:
         if(var10000.equals("toString()Ljava/lang/String;")) {
            return CGLIB$toString$4$Proxy;
         }
         break;
      case 1984935277:
         if(var10000.equals("hashCode()I")) {
            return CGLIB$hashCode$5$Proxy;
         }
      }

      return null;
   }

   public HelloService$$EnhancerByCGLIB$$df0ac471() {
      CGLIB$BIND_CALLBACKS(this);
   }

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

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

   private static final void CGLIB$BIND_CALLBACKS(Object var0) {
      HelloService$$EnhancerByCGLIB$$df0ac471 var1 = (HelloService$$EnhancerByCGLIB$$df0ac471)var0;
      if(!var1.CGLIB$BOUND) {
         var1.CGLIB$BOUND = true;
         Object var10000 = CGLIB$THREAD_CALLBACKS.get();
         if(var10000 == null) {
            var10000 = CGLIB$STATIC_CALLBACKS;
            if(CGLIB$STATIC_CALLBACKS == null) {
               return;
            }
         }

         var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
      }

   }

   public Object newInstance(Callback[] var1) {
      CGLIB$SET_THREAD_CALLBACKS(var1);
      HelloService$$EnhancerByCGLIB$$df0ac471 var10000 = new HelloService$$EnhancerByCGLIB$$df0ac471();
      CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
      return var10000;
   }

   public Object newInstance(Callback var1) {
      CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
      HelloService$$EnhancerByCGLIB$$df0ac471 var10000 = new HelloService$$EnhancerByCGLIB$$df0ac471();
      CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
      return var10000;
   }

   public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
      CGLIB$SET_THREAD_CALLBACKS(var3);
      HelloService$$EnhancerByCGLIB$$df0ac471 var10000 = new HelloService$$EnhancerByCGLIB$$df0ac471;
      switch(var1.length) {
      case 0:
         var10000.<init>();
         CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
         return var10000;
      default:
         throw new IllegalArgumentException("Constructor not found");
      }
   }

   public Callback getCallback(int var1) {
      CGLIB$BIND_CALLBACKS(this);
      MethodInterceptor var10000;
      switch(var1) {
      case 0:
         var10000 = this.CGLIB$CALLBACK_0;
         break;
      default:
         var10000 = null;
      }

      return var10000;
   }

   public void setCallback(int var1, Callback var2) {
      switch(var1) {
      case 0:
         this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
      default:
      }
   }

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

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

   static {
      CGLIB$STATICHOOK1();
   }
}

可以看到代理类HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBdf0ac471继承了目标类HelloService,实现了Factory接口。
可以看到CGLIB除了HelloService类的hello,hi两个方法,也代理Object类的finalize,equals,toString,hashCode,clone五个方法。
代理类为每个目标类的方法生成两个方法,例如针对目标类中的每个非private方法,代理类会生成两个方法,以hello方法为例:一个是@Override的hello方法,一个是CGLIB$hello 0 ( C G L I B 0(CGLIB 0(CGLIBhello$0相当于目标类的hello方法)。在示例代码中调用目标类的方法proxy.hello()时,实际上调用的是代理类中的hello()方法。接下来着重分析代理类中的hello方法,看看是怎么实现的代理功能。

public final String hello(String var1) {
  MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
  if(this.CGLIB$CALLBACK_0 == null) {
     CGLIB$BIND_CALLBACKS(this);
     var10000 = this.CGLIB$CALLBACK_0;
  }

  return var10000 != null?(String)var10000.intercept(this, CGLIB$hello$0$Method, new Object[]{var1}, CGLIB$hello$0$Proxy):super.hello(var1);
}

private static final void CGLIB$BIND_CALLBACKS(Object var0) {
    HelloService$$EnhancerByCGLIB$$df0ac471 var1 = (HelloService$$EnhancerByCGLIB$$df0ac471)var0;
    if(!var1.CGLIB$BOUND) {
        var1.CGLIB$BOUND = true;
        Object var10000 = CGLIB$THREAD_CALLBACKS.get();
        if(var10000 == null) {
            var10000 = CGLIB$STATIC_CALLBACKS;
            if(CGLIB$STATIC_CALLBACKS == null) {
                return;
            }
        }
        
        var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
    }
}

可以看到在CGLIB B I N D C A L L B A C K S 方 法 中 , 从 C G L I B BIND_CALLBACKS方法中,从CGLIB BINDCALLBACKSCGLIBTHREAD_CALLBACKS获取拦截器,若获取不到,则从CGLIB S T A T I C C A L L B A C K S 获 取 , 那 么 拦 截 器 是 如 何 设 置 到 C G L I B STATIC_CALLBACKS获取,那么拦截器是如何设置到CGLIB STATICCALLBACKSCGLIBTHREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?
在JDK动态代理中拦截对象是在实例化代理类时由构造函数传入的,在CGLIB中是调用Enhancer的firstInstance方法来生成代理类实例并设置拦截器的。
firstInstance的调用轨迹为:

  1. Enhancer:firstInstance
  2. Enhancer:createUsingReflection
  3. Enhancer:setThreadCallbacks
  4. Enhancer:setCallbacksHelper
  5. HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBdf0ac471 : CGLIB$SET_THREAD_CALLBACKS

在第5步,调用了代理类的CGLIB S E T T H R E A D C A L L B A C K S 来 完 成 拦 截 对 象 的 注 入 。 下 面 看 一 下 C G L I B SET_THREAD_CALLBACKS来完成拦截对象的注入。下面看一下CGLIB SETTHREADCALLBACKSCGLIBSET_THREAD_CALLBACKS的反编译结果:

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

在CGLIB S E T T H R E A D C A L L B A C K S 方 法 中 调 用 了 C G L I B SET_THREAD_CALLBACKS方法中调用了CGLIB SETTHREADCALLBACKSCGLIBTHREAD_CALLBACKS的set方法来保存拦截对象,在CGLIB B I N D C A L L B A C K S 方 法 中 使 用 了 C G L I B BIND_CALLBACKS方法中使用了CGLIB BINDCALLBACKS使CGLIBTHREAD_CALLBACKS的get方法来获取拦截对象,并保存到CGLIB C A L L B A C K 0 中 。 这 样 , 在 调 用 代 理 类 的 h e l l o 方 法 时 , 就 可 以 获 取 到 我 们 设 置 的 拦 截 对 象 , 然 后 通 过 v a r 10000. i n t e r c e p t ( t h i s , C G L I B CALLBACK_0中。这样,在调用代理类的hello方法时,就可以获取到我们设置的拦截对象,然后通过var10000.intercept(this, CGLIB CALLBACK0hellovar10000.intercept(this,CGLIBhello 0 0 0Method, new Object[]{var1}, CGLIB$hello 0 0 0Proxy)来实现代理。

这里来解释一下intercept方法的参数含义:
@para1 obj:代理对象本身
@para2 method: 被拦截的方法对象
@para3 args:方法调用入参
@para4 proxy:用于调用被拦截方法的方法代理对象

这里会有一个疑问,为什么不直接反射调用代理类生成的(CGLIB$g$0)来间接调用目标类的被拦截方法,而使用proxy的invokeSuper方法呢?这里就涉及到了另外一个点 FastClass 。

FastClass 机制

JDK动态代理的拦截对象是通过反射的机制来调用被拦截方法的,反射的效率比较低,所以CGLIB采用了FastClass机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,下面用一个小例子来说明一下,这样比较直观:

public class FastClass1 {

    public void hello(String name) {
        System.out.println("hello, " + name);
    }
    
    public void hi(String msg) {
        System.out.println("hi, " + msg);
    }
}

public class FastClass2 {
    
    public Object invoke(Object obj, int methodIndex, Object[] parameters) {
        FastClass1 target = (FastClass1)obj;
        Object result = null;
        switch (methodIndex) {
        case 1:
            target.hello((String)parameters[0]);
            break;
        case 2:
            target.hi((String)parameters[0]);
            break;
        }
        return result;
    }
    
    public int getIndex(String methodDescriptor) {
        switch (methodDescriptor.hashCode()) {
        case -2084786067:
            return 1;
        case -70025314:
            return 2;
        }
        return -1;
    }
}

public class FastClassTest {
    public static void main(String[] args) {
        FastClass1 fastClass1 = new FastClass1();
        FastClass2 fastClass2 = new FastClass2();
        int helloIndex = fastClass2.getIndex("hello(Ljava/lang/String;)V");//方法名(参数类型;...)返回类型
        fastClass2.invoke(fastClass1, helloIndex, new Object[]{"张三"});
        int hiIndex = fastClass2.getIndex("hi(Ljava/lang/String;)V");
        fastClass2.invoke(fastClass1, hiIndex, new Object[]{"cglib动态代理"});
    }
}

上例中,FastClass2是FastClass1的Fastclass,在FastClass2中有两个方法getIndex和invoke。在getIndex方法中对FastClass1的每个方法建立索引,并根据入参(方法名+方法的描述符)来返回相应的索引。invoke根据指定的索引,以parameters为入参调用对象obj的方法。这样就避免了反射调用,提高了效率。
代理类(HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBdf0ac47)中与生成Fastclass相关的代码如下:

Class var0 = Class.forName("cglib.HelloService$$EnhancerByCGLIB$$df0ac471");
var1 = Class.forName("cglib.HelloService");
CGLIB$hello$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "hello", "CGLIB$hello$0");

来看下MethodProxy.create方法:

//5个参数分别是
//Class c1 目标类Class
//Class c2 代理类Class
//String desc 代理方法的描述: (参数类型)返回值类型,因为描述中没有方法名,因此这里代理方法描述和被代理方法描述一致
//String name1 被代理方法名
//String name2 代理方法名
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 CreateInfo(c1, c2);
    return proxy;
}

MethodProxy.create中会对var0和var1进行分析并生成FastClass,然后再使用getIndex来获取方法hello和CGLIB$hello$0的索引,具体的生成过程将在后续进行介绍,这里介绍一个关键的内部类:

private static class FastClassInfo
{
    FastClass f1; //真实类的FastClass
    FastClass f2; //代理类的FastClass
    int i1; //真实方法的f1的索引
    int i2; //代理方法的f2的索引
}

MethodProxy中invokeSuper方法的代码如下:

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

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();
                //生成目标类的FastClass
                fci.f1 = helper(ci, ci.c1);
                //生成代理类的FastClass
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(sig1);
                fci.i2 = fci.f2.getIndex(sig2);
                fastClassInfo = fci;
                createInfo = null;
            }
        }
    }
}

private static FastClass helper(CreateInfo ci, Class type) {
    FastClass.Generator g = new FastClass.Generator();
    g.setType(type);
    g.setClassLoader(ci.c2.getClassLoader());
    g.setNamingPolicy(ci.namingPolicy);
    g.setStrategy(ci.strategy);
    g.setAttemptLoad(ci.attemptLoad);
    return g.create();
}

可以看到FastClass生成调用链:

  1. MethodProxy : invokeSuper(Object obj, Object[] args)
  2. MethodProxy : init()
  3. MethodProxy : helper(CreateInfo ci, Class type)
  4. FastClass.Generator : create()

而invokeSuper()方法是在代理类方法调用时才会调用,因此FastClass的生成也采用了类似 lazy-init 的方式提高性能。

在MethodProxy.invokeSuper(Object, Object[])中调用了fci.f2.invoke(fci.i2, obj, args)方法,即代理类FastClass的invoke方法,我们来看下代理类FastClass(每个FastClass都有一个Class类生成,我们配置了Class文件生成,反编译):

package cglib;

import cglib.HelloService..EnhancerByCGLIB..df0ac471;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;

public class HelloService$$EnhancerByCGLIB$$df0ac471$$FastClassByCGLIB$$81065074 extends FastClass {

   public HelloService$$EnhancerByCGLIB$$df0ac471$$FastClassByCGLIB$$81065074(Class var1) {
      super(var1);
   }

   public int getIndex(Signature var1) {
      String var10000 = var1.toString();
      switch(var10000.hashCode()) {
      case -2071771415:
         if(var10000.equals("CGLIB$clone$6()Ljava/lang/Object;")) {
            return 22;
         }
         break;
      case -2055565910:
         if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
            return 13;
         }
         break;
      case -1725733088:
         if(var10000.equals("getClass()Ljava/lang/Class;")) {
            return 26;
         }
         break;
      case -1663710620:
         if(var10000.equals("CGLIB$equals$3(Ljava/lang/Object;)Z")) {
            return 19;
         }
         break;
      case -1457535688:
         if(var10000.equals("CGLIB$STATICHOOK1()V")) {
            return 15;
         }
         break;
      case -1411783143:
         if(var10000.equals("CGLIB$hashCode$5()I")) {
            return 21;
         }
         break;
      case -1408744366:
         if(var10000.equals("CGLIB$hi$1(Ljava/lang/String;)Ljava/lang/String;")) {
            return 17;
         }
         break;
      case -1090657086:
         if(var10000.equals("hi(Ljava/lang/String;)Ljava/lang/String;")) {
            return 7;
         }
         break;
      case -1026001249:
         if(var10000.equals("wait(JI)V")) {
            return 24;
         }
         break;
      case -894172689:
         if(var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
            return 4;
         }
         break;
      case -879968516:
         if(var10000.equals("CGLIB$hello$0(Ljava/lang/String;)Ljava/lang/String;")) {
            return 16;
         }
         break;
      case -623122092:
         if(var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
            return 14;
         }
         break;
      case -419626537:
         if(var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
            return 9;
         }
         break;
      case 243996900:
         if(var10000.equals("wait(J)V")) {
            return 25;
         }
         break;
      case 560567118:
         if(var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
            return 8;
         }
         break;
      case 811063227:
         if(var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
            return 5;
         }
         break;
      case 848333779:
         if(var10000.equals("hello(Ljava/lang/String;)Ljava/lang/String;")) {
            return 6;
         }
         break;
      case 946854621:
         if(var10000.equals("notifyAll()V")) {
            return 28;
         }
         break;
      case 973717575:
         if(var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
            return 11;
         }
         break;
      case 1116248544:
         if(var10000.equals("wait()V")) {
            return 23;
         }
         break;
      case 1221173700:
         if(var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
            return 3;
         }
         break;
      case 1230699260:
         if(var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
            return 10;
         }
         break;
      case 1365107430:
         if(var10000.equals("CGLIB$finalize$2()V")) {
            return 18;
         }
         break;
      case 1584330438:
         if(var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
            return 12;
         }
         break;
      case 1729170762:
         if(var10000.equals("CGLIB$toString$4()Ljava/lang/String;")) {
            return 20;
         }
         break;
      case 1826985398:
         if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
            return 0;
         }
         break;
      case 1902039948:
         if(var10000.equals("notify()V")) {
            return 27;
         }
         break;
      case 1913648695:
         if(var10000.equals("toString()Ljava/lang/String;")) {
            return 1;
         }
         break;
      case 1984935277:
         if(var10000.equals("hashCode()I")) {
            return 2;
         }
      }

      return -1;
   }

   public int getIndex(String var1, Class[] var2) {
      switch(var1.hashCode()) {
      case -2083498449:
         if(var1.equals("CGLIB$finalize$2")) {
            switch(var2.length) {
            case 0:
               return 18;
            }
         }
         break;
      case -1776922004:
         if(var1.equals("toString")) {
            switch(var2.length) {
            case 0:
               return 1;
            }
         }
         break;
      case -1295482945:
         if(var1.equals("equals")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.Object")) {
                  return 0;
               }
            }
         }
         break;
      case -1053468136:
         if(var1.equals("getCallbacks")) {
            switch(var2.length) {
            case 0:
               return 11;
            }
         }
         break;
      case -1039689911:
         if(var1.equals("notify")) {
            switch(var2.length) {
            case 0:
               return 27;
            }
         }
         break;
      case -124978607:
         if(var1.equals("CGLIB$equals$3")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.Object")) {
                  return 19;
               }
            }
         }
         break;
      case -60403779:
         if(var1.equals("CGLIB$SET_STATIC_CALLBACKS")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                  return 12;
               }
            }
         }
         break;
      case -29025553:
         if(var1.equals("CGLIB$hashCode$5")) {
            switch(var2.length) {
            case 0:
               return 21;
            }
         }
         break;
      case 3329:
         if(var1.equals("hi")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 7;
               }
            }
         }
         break;
      case 3641717:
         if(var1.equals("wait")) {
            switch(var2.length) {
            case 0:
               return 23;
            case 1:
               if(var2[0].getName().equals("long")) {
                  return 25;
               }
               break;
            case 2:
               if(var2[0].getName().equals("long") && var2[1].getName().equals("int")) {
                  return 24;
               }
            }
         }
         break;
      case 85179481:
         if(var1.equals("CGLIB$SET_THREAD_CALLBACKS")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                  return 13;
               }
            }
         }
         break;
      case 99162322:
         if(var1.equals("hello")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 6;
               }
            }
         }
         break;
      case 147696667:
         if(var1.equals("hashCode")) {
            switch(var2.length) {
            case 0:
               return 2;
            }
         }
         break;
      case 161998109:
         if(var1.equals("CGLIB$STATICHOOK1")) {
            switch(var2.length) {
            case 0:
               return 15;
            }
         }
         break;
      case 495524492:
         if(var1.equals("setCallbacks")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                  return 9;
               }
            }
         }
         break;
      case 1154623345:
         if(var1.equals("CGLIB$findMethodProxy")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("net.sf.cglib.core.Signature")) {
                  return 14;
               }
            }
         }
         break;
      case 1543336191:
         if(var1.equals("CGLIB$toString$4")) {
            switch(var2.length) {
            case 0:
               return 20;
            }
         }
         break;
      case 1811874389:
         if(var1.equals("newInstance")) {
            switch(var2.length) {
            case 1:
               String var10001 = var2[0].getName();
               switch(var10001.hashCode()) {
               case -845341380:
                  if(var10001.equals("net.sf.cglib.proxy.Callback")) {
                     return 4;
                  }
                  break;
               case 1730110032:
                  if(var10001.equals("[Lnet.sf.cglib.proxy.Callback;")) {
                     return 3;
                  }
               }
            case 2:
            default:
               break;
            case 3:
               if(var2[0].getName().equals("[Ljava.lang.Class;") && var2[1].getName().equals("[Ljava.lang.Object;") && var2[2].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                  return 5;
               }
            }
         }
         break;
      case 1817099975:
         if(var1.equals("setCallback")) {
            switch(var2.length) {
            case 2:
               if(var2[0].getName().equals("int") && var2[1].getName().equals("net.sf.cglib.proxy.Callback")) {
                  return 8;
               }
            }
         }
         break;
      case 1837078673:
         if(var1.equals("CGLIB$hi$1")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 17;
               }
            }
         }
         break;
      case 1891304123:
         if(var1.equals("CGLIB$hello$0")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 16;
               }
            }
         }
         break;
      case 1902066072:
         if(var1.equals("notifyAll")) {
            switch(var2.length) {
            case 0:
               return 28;
            }
         }
         break;
      case 1905679803:
         if(var1.equals("getCallback")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("int")) {
                  return 10;
               }
            }
         }
         break;
      case 1950568386:
         if(var1.equals("getClass")) {
            switch(var2.length) {
            case 0:
               return 26;
            }
         }
         break;
      case 1951977612:
         if(var1.equals("CGLIB$clone$6")) {
            switch(var2.length) {
            case 0:
               return 22;
            }
         }
      }

      return -1;
   }

   public int getIndex(Class[] var1) {
      switch(var1.length) {
      case 0:
         return 0;
      default:
         return -1;
      }
   }

   public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
      df0ac471 var10000 = (df0ac471)var2;
      int var10001 = var1;

      try {
         switch(var10001) {
         case 0:
            return new Boolean(var10000.equals(var3[0]));
         case 1:
            return var10000.toString();
         case 2:
            return new Integer(var10000.hashCode());
         case 3:
            return var10000.newInstance((Callback[])var3[0]);
         case 4:
            return var10000.newInstance((Callback)var3[0]);
         case 5:
            return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
         case 6:
            return var10000.hello((String)var3[0]);
         case 7:
            return var10000.hi((String)var3[0]);
         case 8:
            var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
            return null;
         case 9:
            var10000.setCallbacks((Callback[])var3[0]);
            return null;
         case 10:
            return var10000.getCallback(((Number)var3[0]).intValue());
         case 11:
            return var10000.getCallbacks();
         case 12:
            df0ac471.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
            return null;
         case 13:
            df0ac471.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
            return null;
         case 14:
            return df0ac471.CGLIB$findMethodProxy((Signature)var3[0]);
         case 15:
            df0ac471.CGLIB$STATICHOOK1();
            return null;
         case 16:
            return var10000.CGLIB$hello$0((String)var3[0]);
         case 17:
            return var10000.CGLIB$hi$1((String)var3[0]);
         case 18:
            var10000.CGLIB$finalize$2();
            return null;
         case 19:
            return new Boolean(var10000.CGLIB$equals$3(var3[0]));
         case 20:
            return var10000.CGLIB$toString$4();
         case 21:
            return new Integer(var10000.CGLIB$hashCode$5());
         case 22:
            return var10000.CGLIB$clone$6();
         case 23:
            var10000.wait();
            return null;
         case 24:
            var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
            return null;
         case 25:
            var10000.wait(((Number)var3[0]).longValue());
            return null;
         case 26:
            return var10000.getClass();
         case 27:
            var10000.notify();
            return null;
         case 28:
            var10000.notifyAll();
            return null;
         }
      } catch (Throwable var4) {
         throw new InvocationTargetException(var4);
      }

      throw new IllegalArgumentException("Cannot find matching method/constructor");
   }

   public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
      df0ac471 var10000 = new df0ac471;
      df0ac471 var10001 = var10000;
      int var10002 = var1;

      try {
         switch(var10002) {
         case 0:
            var10001.<init>();
            return var10000;
         }
      } catch (Throwable var3) {
         throw new InvocationTargetException(var3);
      }

      throw new IllegalArgumentException("Cannot find matching method/constructor");
   }

   public int getMaxIndex() {
      return 28;
   }
}

代理类FastClass类内容较为丰富,但是结构较为简单,主要提供了以下几个方法:

public int getIndex(Signature var1);
public int getIndex(String var1, Class[] var2);
public int getIndex(Class[] var1);
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
public Object newInstance(int var1, Object[] var2) throws InvocationTargetException;
public int getMaxIndex();

在MethodProxy.init()中我们看到FastClassInfo的i1和i2域值的来源:

fci.i1 = fci.f1.getIndex(sig1); //sig1存储目标类的方法信息
fci.i2 = fci.f2.getIndex(sig2);//sig2存储代理类的方法信息

仔细看下代理类FastClass类的getIndex方法:

public int getIndex(Signature var1) {
  String var10000 = var1.toString();
  switch(var10000.hashCode()) {
  case -2071771415:
     if(var10000.equals("CGLIB$clone$6()Ljava/lang/Object;")) {
        return 22;
     }
     break;
  case -2055565910:
     if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
        return 13;
     }
     break;
  case -1725733088:
     if(var10000.equals("getClass()Ljava/lang/Class;")) {
        return 26;
     }
     break;
  case -1663710620:
     if(var10000.equals("CGLIB$equals$3(Ljava/lang/Object;)Z")) {
        return 19;
     }
     break;
  case -1457535688:
     if(var10000.equals("CGLIB$STATICHOOK1()V")) {
        return 15;
     }
     break;
  case -1411783143:
     if(var10000.equals("CGLIB$hashCode$5()I")) {
        return 21;
     }
     break;
  case -1408744366:
     if(var10000.equals("CGLIB$hi$1(Ljava/lang/String;)Ljava/lang/String;")) {
        return 17;
     }
     break;
  case -1090657086:
     if(var10000.equals("hi(Ljava/lang/String;)Ljava/lang/String;")) {
        return 7;
     }
     break;
  case -1026001249:
     if(var10000.equals("wait(JI)V")) {
        return 24;
     }
     break;
  case -894172689:
     if(var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
        return 4;
     }
     break;
  case -879968516:
     if(var10000.equals("CGLIB$hello$0(Ljava/lang/String;)Ljava/lang/String;")) {
        return 16;
     }
     break;
  case -623122092:
     if(var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
        return 14;
     }
     break;
  case -419626537:
     if(var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
        return 9;
     }
     break;
  case 243996900:
     if(var10000.equals("wait(J)V")) {
        return 25;
     }
     break;
  case 560567118:
     if(var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
        return 8;
     }
     break;
  case 811063227:
     if(var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
        return 5;
     }
     break;
  case 848333779:
     if(var10000.equals("hello(Ljava/lang/String;)Ljava/lang/String;")) {
        return 6;
     }
     break;
  case 946854621:
     if(var10000.equals("notifyAll()V")) {
        return 28;
     }
     break;
  case 973717575:
     if(var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
        return 11;
     }
     break;
  case 1116248544:
     if(var10000.equals("wait()V")) {
        return 23;
     }
     break;
  case 1221173700:
     if(var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
        return 3;
     }
     break;
  case 1230699260:
     if(var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
        return 10;
     }
     break;
  case 1365107430:
     if(var10000.equals("CGLIB$finalize$2()V")) {
        return 18;
     }
     break;
  case 1584330438:
     if(var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
        return 12;
     }
     break;
  case 1729170762:
     if(var10000.equals("CGLIB$toString$4()Ljava/lang/String;")) {
        return 20;
     }
     break;
  case 1826985398:
     if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
        return 0;
     }
     break;
  case 1902039948:
     if(var10000.equals("notify()V")) {
        return 27;
     }
     break;
  case 1913648695:
     if(var10000.equals("toString()Ljava/lang/String;")) {
        return 1;
     }
     break;
  case 1984935277:
     if(var10000.equals("hashCode()I")) {
        return 2;
     }
  }

  return -1;
}

虽然方法长,但逻辑简单。
Signature的toString方法用于返回方法描述:

//在这里就是代理类 CGLIB$hello$0的方法描述 即CGLIB$hello$0(Ljava/lang/String;)Ljava/lang/String;
public String toString() {
    return name + desc;
}

可以看到以代理类 CGLIB$hello 0 方 法 为 例 , g e t I n d e x 返 回 方 法 索 引 16 , 即 f c i . i 2 = 16 。 因 此 M e t h o d P r o x y . i n v o k e S u p e r 中 f c i . f 2. i n v o k e ( f c i . i 2 , o b j , a r g s ) ; 即 为 H e l l o S e r v i c e 0方法为例,getIndex返回方法索引16,即fci.i2=16。 因此MethodProxy.invokeSuper中fci.f2.invoke(fci.i2, obj, args);即为HelloService 0getIndex16fci.i2=16MethodProxy.invokeSuperfci.f2.invoke(fci.i2,obj,args);HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB d f 0 a c 471 df0ac471 df0ac471 F a s t C l a s s B y C G L I B FastClassByCGLIB FastClassByCGLIB 81065074. i n v o k e ( 16 , H e l l o S e r v i c e 81065074.invoke(16,HelloService 81065074.invoke(16,HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB$df0ac471, [“张三”])。

来看看代理类FastClass的invoke方法:

public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
  df0ac471 var10000 = (df0ac471)var2; //df0ac471就是代理类
  int var10001 = var1; //代理类hello的代理方法CGLIB$hello$0索引 16

  try {
     switch(var10001) {
     case 0:
        return new Boolean(var10000.equals(var3[0]));
     case 1:
        return var10000.toString();
     case 2:
        return new Integer(var10000.hashCode());
     case 3:
        return var10000.newInstance((Callback[])var3[0]);
     case 4:
        return var10000.newInstance((Callback)var3[0]);
     case 5:
        return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
     case 6:
        return var10000.hello((String)var3[0]);
     case 7:
        return var10000.hi((String)var3[0]);
     case 8:
        var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
        return null;
     case 9:
        var10000.setCallbacks((Callback[])var3[0]);
        return null;
     case 10:
        return var10000.getCallback(((Number)var3[0]).intValue());
     case 11:
        return var10000.getCallbacks();
     case 12:
        df0ac471.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
        return null;
     case 13:
        df0ac471.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
        return null;
     case 14:
        return df0ac471.CGLIB$findMethodProxy((Signature)var3[0]);
     case 15:
        df0ac471.CGLIB$STATICHOOK1();
        return null;
     case 16:
        return var10000.CGLIB$hello$0((String)var3[0]); //成功在此调用了代理类的CGLIB$hello$0方法,
     case 17:
        return var10000.CGLIB$hi$1((String)var3[0]);
     case 18:
        var10000.CGLIB$finalize$2();
        return null;
     case 19:
        return new Boolean(var10000.CGLIB$equals$3(var3[0]));
     case 20:
        return var10000.CGLIB$toString$4();
     case 21:
        return new Integer(var10000.CGLIB$hashCode$5());
     case 22:
        return var10000.CGLIB$clone$6();
     case 23:
        var10000.wait();
        return null;
     case 24:
        var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
        return null;
     case 25:
        var10000.wait(((Number)var3[0]).longValue());
        return null;
     case 26:
        return var10000.getClass();
     case 27:
        var10000.notify();
        return null;
     case 28:
        var10000.notifyAll();
        return null;
     }
  } catch (Throwable var4) {
     throw new InvocationTargetException(var4);
  }

  throw new IllegalArgumentException("Cannot find matching method/constructor");
}

而在代理类CGLIB$hello$0方法中直接调用了其父类,也就是目标类的hello方法:

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

至此成功调用原目标类方法。

MethodProxy.invoke(Object obj, Object[] args)

上文我们说了MethodProxy.invokeSuper(Object obj, Object[] args)方法。那么MethodProxy.invoke(Object obj, Object[] args)是什么?
先来看看MethodProxy.invoke(Object obj, Object[] args)的实现:

public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    } catch (IllegalArgumentException e) {
        if (fastClassInfo.i1 < 0)
            throw new IllegalArgumentException("Protected method: " + sig1);
        throw e;
    }
}

可以看到和MethodProxy.invokeSuper(Object obj, Object[] args)较为相似,fci.f1引用的是目标类的FastClass,来看看目标类的FastClass类:

package cglib;

import cglib.HelloService;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

public class HelloService$$FastClassByCGLIB$$37e7b6d0 extends FastClass {

   public HelloService$$FastClassByCGLIB$$37e7b6d0(Class var1) {
      super(var1);
   }

   public int getIndex(Signature var1) {
      String var10000 = var1.toString();
      switch(var10000.hashCode()) {
      case -1725733088:
         if(var10000.equals("getClass()Ljava/lang/Class;")) {
            return 8;
         }
         break;
      case -1090657086:
         if(var10000.equals("hi(Ljava/lang/String;)Ljava/lang/String;")) {
            return 1;
         }
         break;
      case -1026001249:
         if(var10000.equals("wait(JI)V")) {
            return 3;
         }
         break;
      case 243996900:
         if(var10000.equals("wait(J)V")) {
            return 4;
         }
         break;
      case 848333779:
         if(var10000.equals("hello(Ljava/lang/String;)Ljava/lang/String;")) {
            return 0;
         }
         break;
      case 946854621:
         if(var10000.equals("notifyAll()V")) {
            return 10;
         }
         break;
      case 1116248544:
         if(var10000.equals("wait()V")) {
            return 2;
         }
         break;
      case 1826985398:
         if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
            return 5;
         }
         break;
      case 1902039948:
         if(var10000.equals("notify()V")) {
            return 9;
         }
         break;
      case 1913648695:
         if(var10000.equals("toString()Ljava/lang/String;")) {
            return 6;
         }
         break;
      case 1984935277:
         if(var10000.equals("hashCode()I")) {
            return 7;
         }
      }

      return -1;
   }

   public int getIndex(String var1, Class[] var2) {
      switch(var1.hashCode()) {
      case -1776922004:
         if(var1.equals("toString")) {
            switch(var2.length) {
            case 0:
               return 6;
            }
         }
         break;
      case -1295482945:
         if(var1.equals("equals")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.Object")) {
                  return 5;
               }
            }
         }
         break;
      case -1039689911:
         if(var1.equals("notify")) {
            switch(var2.length) {
            case 0:
               return 9;
            }
         }
         break;
      case 3329:
         if(var1.equals("hi")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 1;
               }
            }
         }
         break;
      case 3641717:
         if(var1.equals("wait")) {
            switch(var2.length) {
            case 0:
               return 2;
            case 1:
               if(var2[0].getName().equals("long")) {
                  return 4;
               }
               break;
            case 2:
               if(var2[0].getName().equals("long") && var2[1].getName().equals("int")) {
                  return 3;
               }
            }
         }
         break;
      case 99162322:
         if(var1.equals("hello")) {
            switch(var2.length) {
            case 1:
               if(var2[0].getName().equals("java.lang.String")) {
                  return 0;
               }
            }
         }
         break;
      case 147696667:
         if(var1.equals("hashCode")) {
            switch(var2.length) {
            case 0:
               return 7;
            }
         }
         break;
      case 1902066072:
         if(var1.equals("notifyAll")) {
            switch(var2.length) {
            case 0:
               return 10;
            }
         }
         break;
      case 1950568386:
         if(var1.equals("getClass")) {
            switch(var2.length) {
            case 0:
               return 8;
            }
         }
      }

      return -1;
   }

   public int getIndex(Class[] var1) {
      switch(var1.length) {
      case 0:
         return 0;
      default:
         return -1;
      }
   }

   public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
      HelloService var10000 = (HelloService)var2;
      int var10001 = var1;

      try {
         switch(var10001) {
         case 0:
            return var10000.hello((String)var3[0]);
         case 1:
            return var10000.hi((String)var3[0]);
         case 2:
            var10000.wait();
            return null;
         case 3:
            var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
            return null;
         case 4:
            var10000.wait(((Number)var3[0]).longValue());
            return null;
         case 5:
            return new Boolean(var10000.equals(var3[0]));
         case 6:
            return var10000.toString();
         case 7:
            return new Integer(var10000.hashCode());
         case 8:
            return var10000.getClass();
         case 9:
            var10000.notify();
            return null;
         case 10:
            var10000.notifyAll();
            return null;
         }
      } catch (Throwable var4) {
         throw new InvocationTargetException(var4);
      }

      throw new IllegalArgumentException("Cannot find matching method/constructor");
   }

   public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
      HelloService var10000 = new HelloService;
      HelloService var10001 = var10000;
      int var10002 = var1;

      try {
         switch(var10002) {
         case 0:
            var10001.<init>();
            return var10000;
         }
      } catch (Throwable var3) {
         throw new InvocationTargetException(var3);
      }

      throw new IllegalArgumentException("Cannot find matching method/constructor");
   }

   public int getMaxIndex() {
      return 10;
   }
}

同样是FastClass类结构是相似的,抽离出invoke,我们细看:

public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
  HelloService var10000 = (HelloService)var2;
  int var10001 = var1;

  try {
     switch(var10001) {
     case 0:
        return var10000.hello((String)var3[0]);
     case 1:
        return var10000.hi((String)var3[0]);
     case 2:
        var10000.wait();
        return null;
     case 3:
        var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
        return null;
     case 4:
        var10000.wait(((Number)var3[0]).longValue());
        return null;
     case 5:
        return new Boolean(var10000.equals(var3[0]));
     case 6:
        return var10000.toString();
     case 7:
        return new Integer(var10000.hashCode());
     case 8:
        return var10000.getClass();
     case 9:
        var10000.notify();
        return null;
     case 10:
        var10000.notifyAll();
        return null;
     }
  } catch (Throwable var4) {
     throw new InvocationTargetException(var4);
  }

  throw new IllegalArgumentException("Cannot find matching method/constructor");
}

这里就会出现死循环。
以目标类hello()方法为例,调用目标类FastClass类invoke(0, HelloService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBdf0ac471, [”张三“]),注意这里var2仍是代理类(方法拦截器的interceptor()方法参数没变),因此HelloService var10000 = (HelloService)var2;不会出错,接下来匹配到case 0,继续调用代理类的hello()方法,但是此次调用的源头就是用户用代理类调用hello()方法,死循环。

分析CGLIB动态代理碰到的问题

主要是跑demo碰到的问题。

问题1:异常Exception in thread “main” java.lang.NoClassDefFoundError: org/objectweb/asm/Type

没用引入asm-xxx.jar包。
CGLIB底层通过asm框架生成字节码文件。maven能够引入传递依赖,因此只要依赖cglib就行;否则只能手动引入。

问题2

image
cglib和asm的jar包冲突,适当降低cglib jar包版本,这里从cglib-3.2.8.jar降到cglib-2.2.2.jar。

问题3

方法拦截器为

public class HelloServiceInterceptor implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class<?> clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		System.out.println("动态代理预处理...");
		System.out.println("代理类:" + arg0);
		System.out.println("被拦截方法:" + arg1.getName());
		Object result = arg3.invokeSuper(arg0, arg2);
		System.out.println(result);
		System.out.println("动态代理后处理...");
		return result;
	}

}

出现死循环,并且栈溢出:
image
根据上文分析,已经很容易判断哪里出问题了?
System.out.println(“代理类:” + arg0);这里有问题,这里arg0是代理类,其实这里调用了arg0.toString()方法。因为CGLIB会默认代理Object类的以下方法:

int hashCode();
public boolean equals(Object obj);
protected native Object clone() throws CloneNotSupportedException;
public String toString();
protected void finalize() throws Throwable;

因此,在方法拦截器的intercept方法中调用以上方法,又会通过代理类的该方法重新调用回方法拦截器的intercept方法,造成栈溢出的死循环。

总结

本文基于CGLIB动态代理的一个demo讨论了CGLIB动态代理的基本原理。

  1. CGLIB动态代理底层通过ASM字节码操纵框架动态生成代理类及其相关类。
  2. CGLIB动态代理会动态生成3个Class,分别是代理类,代理类的FastClass类,目标类的FastClass类。
  • 目标类名 E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBhashcode 代理类
  • 目标类名 F a s t C l a s s B y C G L I B FastClassByCGLIB FastClassByCGLIBhashcode 目标类FastClass类
  • 目标类名 E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBhashcode F a s t C l a s s B y C G L I B FastClassByCGLIB FastClassByCGLIBhashcode 代理类FastClass类
  1. CGLIB默认会代理Object类的5个方法(hashCode ,equals,clone,toString,finalize)和目标类中非private(代理类会调用父类即目标类的方法,显然private方法不允许在子类调用),非final方法(代理类要重写代理方法,显然final方法不能重写)
  2. 代理类继承目标类,重写代理方法,并在代理方法中调用方法拦截器的intercept方法,最终通过FastClass方式调用代理类中与代理方法相对应的方法(CGLIB 代 理 方 法 名 代理方法名 x),并在该方法中调用父类的最终方法。
  3. CGLIB通过FastClass机制加快方法调用速度,以弥补反射调用的不足;
    如何通过代理类的代理方法高效率的访问到目标类的实际方法?
    通过代理类代理方法的索引(代理方法描述的hashcode)直接定位到代理类中与代理方法相对应的方法,在这个方法中调用了父类及目标类的实际方法。
  4. FastClass类的生成延迟到用户调用代理类的代理方法时,并通过缓存的方式,保证每个类(就针对3个类生成)只生成一次。
  5. 代理类Class在Enhancer.create()时生成,并通过
    -Dcglib.debugLocation=H:\EclipseWorkspace\proxy-test\bin\cglib

    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “H:\EclipseWorkspace\proxy-test\bin\cglib”);
    控制3个Class文件的生成。

参考:

深入理解CGLIB动态代理机制
CGLIB(Code Generation Library)详解
cglib源码分析(一): 缓存和KEY
cglib源码分析(二):Class name 生成策略
cglib源码分析(三):Class生成策略
cglib源码分析(四):cglib 动态代理原理分析
代理9 cglib demo分析以及methodProxy与Fastclass源码

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值