• Cglib动态代理实现原理


    Cglib动态代理实现方式

    我们先通过一个demo看一下Cglib是如何实现动态代理的。

    首先定义个服务类,有两个方法并且其中一个方法用final来修饰。

    public class PersonService {
        public PersonService() {
            System.out.println("PersonService构造");
        }
        //该方法不能被子类覆盖
        final public Person getPerson(String code) {
            System.out.println("PersonService:getPerson>>"+code);
            return null;
        }
    
        public void setPerson() {
            System.out.println("PersonService:setPerson");
        }
    }

    Cglib是无法代理final修饰的方法的,具体原因我们一会通过源码来分析。

    然后,定义一个自定义MethodInterceptor。

    public class CglibProxyIntercepter implements MethodInterceptor {
        @Override
        public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("执行前...");
            Object object = methodProxy.invokeSuper(sub, objects);
            System.out.println("执行后...");
            return object;
        }
    }

    我们看一下intercept方法入参,sub:cglib生成的代理对象,method:被代理对象方法,objects:方法入参,methodProxy:代理方法

    最后,我们写个例子调用一下,并将Cglib生成的代理类class文件输出磁盘方便我们反编译查看源码。

    public class Test {
        public static void main(String[] args) {
    //代理类class文件存入本地磁盘 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
    "D:\\code"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback(new CglibProxyIntercepter()); PersonService proxy= (PersonService) enhancer.create();
    proxy.setPerson();
    proxy.getPerson("1");
    } }

     我们执行一下会发现getPerson因为加final修饰并没有被代理,下面我们通过源码分析一下。

    执行前...
    PersonService:setPerson
    执行后...
    PersonService:getPerson>>1

    生成代理类

     执行Test测试类可以得到Cglib生成的class文件,一共有三个class文件我们反编译以后逐个说一下他们的作用。

    PersonService$$EnhancerByCGLIB$$eaaaed75就是cglib生成的代理类,它继承了PersonService类。
    public class PersonService$$EnhancerByCGLIB$$eaaaed75
      extends PersonService
      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$setPerson$0$Method;//被代理方法
      private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法
      private static final Object[] CGLIB$emptyArgs;
      private static final Method CGLIB$finalize$1$Method;
      private static final MethodProxy CGLIB$finalize$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 localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理类
        Class localClass2;//被代理类PersionService
        Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = tmp95_92[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
        Method[] tmp115_95 = tmp95_92;
        CGLIB$equals$2$Method = tmp115_95[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        Method[] tmp135_115 = tmp115_95;
        CGLIB$toString$3$Method = tmp135_115[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        Method[] tmp155_135 = tmp135_115;
        CGLIB$hashCode$4$Method = tmp155_135[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
        Method[] tmp175_155 = tmp155_135;
        CGLIB$clone$5$Method = tmp175_155[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        tmp175_155;
        Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods());
        CGLIB$setPerson$0$Method = tmp223_220[0];
        CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");
        tmp223_220;
        return;
      }
      

    我们通过代理类的源码可以看到,代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应,比如 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;

    方法的调用

     
    //代理方法(methodProxy.invokeSuper会调用)
    final void CGLIB$setPerson$0() { super.setPerson(); } //被代理方法(methodProxy.invoke会调用,这就是为什么在拦截器中调用methodProxy.invoke会死循环,一直在调用拦截器) public final void setPerson() { 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$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy); } else { super.setPerson(); } }

    调用过程:代理对象调用this.setPerson方法->调用拦截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理对象setPerson方法

    MethodProxy

    拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的,MethodProxy非常关键,我们分析一下它具体做了什么。

    • 创建MethodProxy
    public class MethodProxy {
        private Signature sig1;
        private Signature sig2;
        private MethodProxy.CreateInfo createInfo;
        private final Object initLock = new Object();
        private volatile MethodProxy.FastClassInfo fastClassInfo;
        //c1:被代理对象Class
    //c2:代理对象Class
    //desc:入参类型
    //name1:被代理方法名
    //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 MethodProxy.CreateInfo(c1, c2); return proxy; }
    private static class CreateInfo {
    Class c1;
    Class c2;
    NamingPolicy namingPolicy;
    GeneratorStrategy strategy;
    boolean attemptLoad;

    public CreateInfo(Class c1, Class c2) {
    this.c1 = c1;
    this.c2 = c2;
    AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
    if(fromEnhancer != null) {
    this.namingPolicy = fromEnhancer.getNamingPolicy();
    this.strategy = fromEnhancer.getStrategy();
    this.attemptLoad = fromEnhancer.getAttemptLoad();
    }

    }
    }
     
    • invokeSuper调用
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
    private static class FastClassInfo {
    FastClass f1;//被代理类FastClass
    FastClass f2;//代理类FastClass
    int i1; //被代理类的方法签名(index)
    int i2;//代理类的方法签名

    private FastClassInfo() {
    }
    }
     
    上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class文件吗?PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理类的FastClass,
    PersonService$$FastClassByCGLIB$$a076b035.class就是被代理类的FastClass。

    FastClass机制

     Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。
    这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。下面我们反编译一个FastClass看看:
     //根据方法签名获取index
    public int getIndex(Signature var1) { String var10000 = var1.toString(); switch(var10000.hashCode()) { case -2077043409: if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) { return 21; } break; case -2055565910: if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) { return 12; } break; case -1902447170: if(var10000.equals("setPerson()V")) { return 7; } break; //省略部分代码.....
     
    //根据index直接定位执行方法
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { eaaaed75 var10000 = (eaaaed75)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((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]); case 4: return var10000.newInstance((Callback)var3[0]); case 5: return var10000.newInstance((Callback[])var3[0]); case 6: var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]); return null; case 7: var10000.setPerson(); return null; case 8: var10000.setCallbacks((Callback[])var3[0]); return null; case 9: return var10000.getCallback(((Number)var3[0]).intValue()); case 10: return var10000.getCallbacks(); case 11: eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]); return null; case 12: eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]); return null; case 13: return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]); case 14: return var10000.CGLIB$toString$3(); case 15: return new Boolean(var10000.CGLIB$equals$2(var3[0])); case 16: return var10000.CGLIB$clone$5(); case 17: return new Integer(var10000.CGLIB$hashCode$4()); case 18: var10000.CGLIB$finalize$1(); return null; case 19: var10000.CGLIB$setPerson$0(); return null; //省略部分代码.... } catch (Throwable var4) { throw new InvocationTargetException(var4); } throw new IllegalArgumentException("Cannot find matching method/constructor"); }

     FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放在了缓存中。

    //MethodProxy invoke/invokeSuper都调用了init()
    private
    void init() { if(this.fastClassInfo == null) { Object var1 = this.initLock; synchronized(this.initLock) { if(this.fastClassInfo == null) { MethodProxy.CreateInfo ci = this.createInfo; MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo(); fci.f1 = helper(ci, ci.c1);//如果缓存中就取出,没有就生成新的FastClass fci.f2 = helper(ci, ci.c2); fci.i1 = fci.f1.getIndex(this.sig1);//获取方法的index fci.i2 = fci.f2.getIndex(this.sig2); this.fastClassInfo = fci; this.createInfo = null; } } } }
    至此,Cglib动态代理的原理我们就基本搞清楚了,代码细节有兴趣可以再研究下。
    最后我们总结一下JDK动态代理和Gglib动态代理的区别:
    1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
    2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
    3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。


  • 相关阅读:
    射频系统架构
    Tensorflow 2.0 mnist
    Lintcode 32. 最小子串覆盖 && Leetcode 76. Minimum Window Substring
    内存管理之智能指针unique_ptr&weak_ptr
    内存管理之智能指针shared_ptr
    内存管理之直接内存管理
    c++ 三大特性之继承
    稀疏矩阵乘法
    Linux环境下mysql常用命令
    字符串的查找删除
  • 原文地址:https://www.cnblogs.com/monkey0307/p/8328821.html
Copyright © 2020-2023  润新知