CGLIB 动态代理用例及源码解析

CGLIB 动态代理

参考链接:https://blog.csdn.net/yhl_jxy/article/details/80633194
参考链接:https://www.jianshu.com/p/001f866a49d7

1.CGLIB 简单介绍

CGLIB简介:CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用。

2.Demo
2.1 创建需要被代理的类
public class Student2 {


    public String getName() {
        System.out.println("我叫红领巾");
        return "我叫红领巾";
    }

    public Integer getAge() {
        System.out.println("14");
        return 14;
    }
}

2.2 生成代理类 需实现MethodInterceptor接口
public class MyProxy implements MethodInterceptor {

   	/**
   	 * Enhancer.create(superClass,callback)
     * superClass: 生成代理对象的父类
     * callback:设置enhancer的回调对象
     **/
    public <T> T getProxy(Class<T> clazz){
        return (T) Enhancer.create(clazz,this);
    }

    /**
     * target:cglib生成的代理对象
     * method:被代理对象方法
     * args:方法入参
     * methodProxy: 代理方法
     **/
     @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
         System.out.println("前置");
         //用MethodProxy 调用父类方法。
        Object returnObject =  methodProxy.invokeSuper(target,args);
         System.out.println("后置");
        return returnObject;
    }
}
2.3 生成测试类
public class ProxyTest {
    public static void main(String[] args) {
       // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/admin");
        MyProxy myProxy = new MyProxy();
        Student2 student2 =  myProxy.getProxy(Student2.class);
        student2.getName();
    }
}

2.4 运行结果运行结果
2.5 生成的代理类class文件代理类class文件
3. 源码解析

在Demo中通过代理类 Enhancer.create(clazz,this)方法返回被代理类

public static Object create(Class type, Callback callback) {
        Enhancer e = new Enhancer();
        e.setSuperclass(type);
        e.setCallback(callback);
        return e.create();
    }

该方法就是创建一个Enhancer类通过create方法返回被代理类

public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();
    }

该方法含义就是如果有必要就创建一个新类,并且用指定的回调对象创建一个新的对象实例,

使用的父类的参数的构造方法来实例化父类的部分。核心内容在createHelper()中,源码如下:

private Object createHelper() {
        this.preValidate();
        Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, 
          ReflectUtils.getNames(this.interfaces),
          this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), 
          this.callbackTypes, this.useFactory, 
          this.interceptDuringConstruction,
          this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

preValidate()方法校验callbackTypes、filter是否为空,以及为空时的处理。
通过newInstance()方法创建EnhancerKey对象,作为Enhancer父类AbstractClassGenerator.create()方法

protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            // 如果 data 不存在 ,在多线程环境下添加到CACHE
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }

            this.key = key;
            Object obj = data.get(this, this.getUseCache());
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        } catch (Error | RuntimeException var9) {
            throw var9;
        } catch (Exception var10) {
            throw new CodeGenerationException(var10);
        }
    }

真正创建代理对象方法在nextInstance()方法中,该方法为抽象类AbstractClassGenerator的一个方法,签名如下:
abstract protected Object nextInstance(Object instance) throws Exception;

在子类Enhancer中实现,实现源码如下:


protected Object nextInstance(Object instance) {
        EnhancerFactoryData data = (EnhancerFactoryData) instance;
 
        if (classOnly) {
            return data.generatedClass;
        }
 
        Class[] argumentTypes = this.argumentTypes;
        Object[] arguments = this.arguments;
        if (argumentTypes == null) {
            argumentTypes = Constants.EMPTY_CLASS_ARRAY;
            arguments = null;
        }
        return data.newInstance(argumentTypes, arguments, callbacks);
    }

通过data.newInstance(argumentTypes, arguments, callbacks)方法,
第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象。
最后根据这些参数,通过反射生成代理对象,源码如下:

/**
         * Creates proxy instance for given argument types, and assigns the callbacks.
         * Ideally, for each proxy class, just one set of argument types should be used,
         * otherwise it would have to spend time on constructor lookup.
         * Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
         * with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
         *
         * @see #createUsingReflection(Class)
         * @param argumentTypes constructor argument types
         * @param arguments constructor arguments
         * @param callbacks callbacks to set for the new instance
         * @return newly created proxy
         */
        public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
            setThreadCallbacks(callbacks);
            try {
                // Explicit reference equality is added here just in case Arrays.equals does not have one
                if (primaryConstructorArgTypes == argumentTypes ||
                        Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
                    // If we have relevant Constructor instance at hand, just call it
                    // This skips "get constructors" machinery
                    return ReflectUtils.newInstance(primaryConstructor, arguments);
                }
                // Take a slow path if observing unexpected argument types
                return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
            } finally {
                // clear thread callbacks to allow them to be gc'd
                setThreadCallbacks(null);
            }

        }

生成的代理对象的class文件进行反编译之后的代码如下

public class Student2$$EnhancerByCGLIB$$3859af2f extends Student2 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$getName$0$Method;
  
  private static final MethodProxy CGLIB$getName$0$Proxy;
  
  private static final Object[] CGLIB$emptyArgs;
  
  private static final Method CGLIB$getAge$1$Method;
  
  private static final MethodProxy CGLIB$getAge$1$Proxy;
  
  private static final Method CGLIB$equals$2$Method;
  
  private static final MethodProxy CGLIB$equals$2$Proxy;
  
  private static final Method CGLIB$toString$3$Method;
  
  private static final MethodProxy CGLIB$toString$3$Proxy;
  
  private static final Method CGLIB$hashCode$4$Method;
  
  private static final MethodProxy CGLIB$hashCode$4$Proxy;
  
  private static final Method CGLIB$clone$5$Method;
  
  private static final MethodProxy CGLIB$clone$5$Proxy;
  
  static void CGLIB$STATICHOOK1() {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class clazz1 = Class.forName("com.domain.Student2$$EnhancerByCGLIB$$3859af2f");
    Class clazz2;
    CGLIB$equals$2$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[0];
    CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
    CGLIB$toString$3$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[1];
    CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
    CGLIB$hashCode$4$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[2];
    CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz1, "()I", "hashCode", "CGLIB$hashCode$4");
    CGLIB$clone$5$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[3];
    CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$getName$0$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[0];
    CGLIB$getName$0$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "getName", "CGLIB$getName$0");
    CGLIB$getAge$1$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[1];
    CGLIB$getAge$1$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/Integer;", "getAge", "CGLIB$getAge$1");
    ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods());
  }
  
  final String CGLIB$getName$0() { return super.getName(); }
  
  public final String getName() {
    if (this.CGLIB$CALLBACK_0 == null) {
      this.CGLIB$CALLBACK_0;
      CGLIB$BIND_CALLBACKS(this);
    } 
    return (this.CGLIB$CALLBACK_0 != null) ? (String)this.CGLIB$CALLBACK_0.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy) : super.getName();
  }
  
  		...................
		...................
}

从上述代码可以看到 如果被代理类调用getName()方法,就会执行 代理类的intercept()方法


//  CGLIB$getName$0$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[0];
//  CGLIB$getName$0$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "getName", "CGLIB$getName$0");


clazz2 = Class.forName("com.domain.Student2"))
clazz1 =  Class.forName("com.domain.Student2$$EnhancerByCGLIB$$3859af2f");

//下面看下MethodProxy.create()方法
c1:  Student
c2: Student$$Enhancer
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;
    }

   
this.CGLIB$CALLBACK_0.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy) 

在MethodProxy中

对象实例
c1Student2
f1c1的fastClass,更方便调用c1的函数
c2Student2$$Enhancer,生成的代理对象的实例
f2c2的fastClass,更加方便调用c2的函数

在intercept()方法中,调用MethodProxy的invokeSuper()方法

 public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
         System.out.println("前置");
        Object returnObject =  methodProxy.invokeSuper(target,args);
         System.out.println("后置");
        return returnObject;
    }

invokeSuper()

   public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //通过下面的代码分析  可知此处调用是 Student$$Enhancer$$FastClss的invoke方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
//下面是init() 方法
private void init() {
       if (this.fastClassInfo == null) {
           synchronized(this.initLock) {
               if (this.fastClassInfo == null) {
                   MethodProxy.CreateInfo ci = this.createInfo;
                   MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                   // f1对应的是 Student的FastClass
                   fci.f1 = helper(ci, ci.c1);
                   // f2对应的是 Student$$Enhancer 的FastClass
                   fci.f2 = helper(ci, ci.c2);
                   
                   //i1i2分别是分别在Student$$FastClass和Student$$Enhancer$$FastClss中 调用方法的index.
                   fci.i1 = fci.f1.getIndex(this.sig1);
                   fci.i2 = fci.f2.getIndex(this.sig2);
                   this.fastClassInfo = fci;
                   this.createInfo = null;
               }
           }
       }
   }
  //init() 中又调用helper()方法
 //根据ci得到该方法的委托类,实现类,分别生成这两个类的fastClass
  private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
       Generator g = new Generator();
       g.setType(type);
       g.setContextClass(type);
       g.setClassLoader(ci.c2.getClassLoader());
       g.setNamingPolicy(ci.namingPolicy);
       g.setStrategy(ci.strategy);
       g.setAttemptLoad(ci.attemptLoad);
       return g.create();
   }

通过反编译得到Student$$EnchancerFastClass的invoke()方法

public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException {
    try {
      switch (paramInt) {
        case 0:
          return new Boolean(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).equals(paramArrayOfObject[0]));
        case 1:
          return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).toString();
          
        ..........
         //找到Student2$$Enhancer 的getName$0()方法。
        case 17:
          return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$getName$0();
        case 18:
          return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$getAge$1();
        case 19:
          return new Boolean(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$equals$2(paramArrayOfObject[0]));
        case 20:
          return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$toString$3();
        case 21:
          return new Integer(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$hashCode$4());
        case 22:
          return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$clone$5();
      } 
    } catch (Throwable throwable) {
      throw new InvocationTargetException(null);
    } 
    throw new IllegalArgumentException("Cannot find matching method/constructor");
  }

在Student$ E n h a n c e r 中 调 用 C G L I B Enhancer中调用CGLIB EnhancerCGLIBgetName$0()调用 super. name()方法 即 Student 的getName方法();

final String CGLIB$getName$0() { return super.getName(); }
思考

思考
1.fastclass比反射快的原因
通过方法前面或者标识符index,利用switch case直接利用对象去调用函数
而反射是java.lang.reflect.Method#invoke,稍微复杂点,这个没研究过具体实现

2.MethodProxy#invoke和MethodProxy#invokeSuper什么区别,即[c1,f1]与[c2,f2]的区别
[c1,f1]对应的是 父类的class和fastclass
[c2,f2]对应的是 父类的enhanceClass和 enhanceFastClass

3.MethodProxy#init创建fastclass时,每个method在第一次调用时,都会进行
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create
那么为什么对应的fastclass文件只生成了一次(不是一个method调用一次就生成一次)
并且一次就有整个类的信息,而不是只有这个method相关信息呢

第一点:同一个类的fastClass只生成了一次,
net.sf.cglib.reflect.FastClass.Generator#create
net.sf.cglib.core.AbstractClassGenerator#create
里面用了缓存
第二点:一次就有整个类的信息,而不是只有这个method信息
net.sf.cglib.proxy.MethodProxy#create时就传入和class c1,c2
后来创建fastClass时
net.sf.cglib.proxy.MethodProxy#helper
调用了g.setType(type);
在fastClass生成时
net.sf.cglib.reflect.FastClass.Generator#generateClass
用到了这个之前设置好的Class type,也就直到类信息了
invokeSuper的逻辑

4.把invokeSuper改成invoke会怎么样
结论:死循环,堆栈溢出

访问Student E n h a n c e r 中 g e t N a m e ( ) 方 法 然 后 进 入 M e t h o d I n t e r c e p t o r 的 i n t e r c e p t o r ( ) 方 法 又 去 调 用 S t u d e n t Enhancer中 getName()方法 然后进入MethodInterceptor的interceptor()方法 又去调用Student EnhancergetName(MethodInterceptorinterceptor()StudentEnhancer中 getName()方法 …
导致死循环

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值