動態代理-cglib分析

生成代理類文件的方式

jvm添加此啓動參數,後面就是代理類class生成的地址
-Dcglib.debugLocation=~/baldhead/java/dynamic-proxy-cglib/src/main/java/com/baldhead/dynamic/proxy/cglib/class

添加這個參數之後,CGLIB就會把生成的代理Class文件存在指定的路徑

生成動態代理對象流程

  1. CGLIB首先生成代理類
  2. 代碼中的 static 靜態代碼塊 會調用 CGLIB$STATICHOOK1(); 方法,方法作用
    3. 新建一個名字爲 CGLIB$THREAD_CALLBACKSThreadLocal,用來存放所設置的 callback
    4. 使用反射找到代理類中的所有方法,包括(toStringhashCodeequalsclone),名字爲模板 CGLIB$METHODNAME$數字編號$Method
    並且給對應的方法創建代理方法 名字模板CGLIB$METHODNAME$數字編號$Proxy
  3. 調用構造方法創建代理對象
  4. 然後CGLIB會調用代理對象的 CGLIB$SET_THREAD_CALLBACKS 方法,將傳入的 callBack存到 ThreadLocal(CGLIB$THREAD_CALLBACKS) 中去
  5. 後續在對象執行需要代理的方法的時候,就會從CGLIB$THREAD_CALLBACKS中拿到所設置的 CallBack並調用它的intercept()方法

代理對象的創建

static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService$$EnhancerByCGLIB$$e34eec9a");
        Class var1;
        CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService")).getDeclaredMethods())[0];
        CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
    }

以上代碼經過簡化的,主要看下面給出的一行
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
對應的方法如下

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        /**
         * 這幾個參數都可以找到入參對象
         * c1: 被代理類對象的class,也就是原始對象的class
         * c2: 代理類對象的 class
         * desc: 方法的返回值類型
         * name1: 原始代理方法的名稱
         * name2: 代理方法在代理類中的名稱(CGLIB$test$0)
         */
        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中有三個很重要的屬性

  • sig1: 表示test方法
  • sig2: 表示 CGLIB$test$0 方法
  • createInfo: 表示原始類和代理類

invoke和invokeSuper方法

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

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

兩個方法大差不差的,但是都用到了一個對象 fastClassInfo 這個對象是在 init()方法中構造的

private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

fastClassInfo對象中主要是有四個屬性

  • f1: 原始類對應的一個FastClass 代理對象
  • f2: 代理類對應的一個FastClass 代理對象
  • i1: test方法在原始類對應的一個FastClass代理對象中的下標
  • i2: CGLIB$test$0方法在代理類對應的一個 FastClass 代理對象中的下標
    這裏產生了兩個代理對象,你說好巧不巧,正好產生的代理,class有3個,其中有兩個繼承 FastClass, 另外一個繼承原始類並且實現 Factory接口

image.png
其實這兩個類類似,都是針對某一個類的FastClass代理類,所以我們好好看一下UserService所對應的FastClass該類主要有:

  1. 一個構造方法
  2. public int getlndex(Signature var1)
  3. public int getlndex(String var1, Classll var2)
  4. public int getlndex(ClassI var1)
  5. public Object invoke(int var1, Object ar2, Objectll var3)
  6. public Object newlnstance(int var1, Objectll var2)
  7. public int getMaxlndex0

顧名思義,FastClass的作用是提高方法的執行速度,按照正常的實現,當我們調用MethodProxy對象的invokel或invokeSuper0方法時,首先應該要做到的就是找到對應的Method對象,比如:

  1. 執行invoke0,要找到test方法對應的Method對象

  2. 執行invokeSuper0,要找到CGLIBstest$00方法對應的Method對象然後利用反射來執行Method。

那麼FastClass的機制就是預先把UserService類或UserService代理類中的所有方法做一個索引,比如:

  public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch (var10000.hashCode()) {
            case -2055565910:
                if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                    return 19;
                }
                break;
            case -1659690448:
                if (var10000.equals("CGLIB$test$4()V")) {
                    return 20;
                }
                break;
            case -1457535688:
                if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                    return 12;
                }
                break;
            case -1422510685:
                if (var10000.equals("test()V")) {
                    return 7;
                }
                break;
            case -1411872516:
                if (var10000.equals("CGLIB$hashCode$2()I")) {
                    return 15;
                }
                break;
        // 省略部分代碼
        }

        return -1;
    }

一旦調用 getIndex(Signature var1) 方法,就對得到對應方法返回的索引,例如這裏就是test方法返回的對應的索引就是7
再回到init 方法

private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

init方法中的兩個 helper方法就是去生成原始類和代理類的 FactClass代理類,後面個兩個getIndex方法
1. 第一個fci.f1.getIndex(this.sig1)就是去獲取原始類對應的FastClass代理類中 test方法的下標i1
2. 第二個 fci.f2.getIndex(this.sig2)就是去獲取代理類對應的FastClass代理類中$test$0方法的下標i2

然後會把兩個下標都記錄在 fastClassInfo 對象中

後面就是我們看到的invokeinvokeSuper中調用的兩個方法

  • invoke

    • fci.f1.invoke(fci.i1, obj, args);

      執行原始類對應的FastClass 代理類的invoke方法

  • invokeSuper

    • fci.f2.invoke(fci.i2, obj, args);

      執行代理類對應的FastClass代理類的invoke方法

例如: 原始類對應的FastClass 代碼

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

        try {
            switch (var10001) {
                case 0:
                    var10000.test();
                    return null;
                case 1:
                    return new Boolean(var10000.equals(var3[0]));
                case 2:
                    return var10000.toString();
                case 3:
                    return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

這個代碼比較簡單,第一個參數就是執行方法的index,第二個參數就是原始類,第三個就是原始類的參數

如果傳入的index 是0 ,那麼就會去執行test方法

代理類對應的FastClass代理類的invoke方法也是類似

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserService..EnhancerByCGLIB..e34eec9a var10000 = (UserService..EnhancerByCGLIB..e34eec9a)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.clone();
                case 4:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 5:
                    return var10000.newInstance((Callback[])var3[0]);
                case 6:
                    return var10000.newInstance((Callback)var3[0]);
                case 7:
                    var10000.test();
                    return null;
                case 8:
                    e34eec9a.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 9:
                    e34eec9a.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 10:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 11:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 12:
                    return var10000.getCallbacks();
                case 13:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 14:
                    return e34eec9a.CGLIB$findMethodProxy((Signature)var3[0]);
                case 15:
                    e34eec9a.CGLIB$STATICHOOK1();
                    return null;
                case 16:
                    var10000.CGLIB$test$0();
                    return null;
                case 17:
                    return new Integer(var10000.CGLIB$hashCode$3());
                case 18:
                    return new Boolean(var10000.CGLIB$equals$1(var3[0]));
                case 19:
                    return var10000.CGLIB$toString$2();
                case 20:
                    return var10000.CGLIB$clone$4();
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

例如傳入的index 是16 那麼執行的就是 var10000.CGLIB$test$0();

如果傳入的index是 7 那麼執行的就是var10000.test();

var10000 是傳入對象強轉爲UserService..EnhancerByCGLIB..e34eec9a類的對象,UserService..EnhancerByCGLIB..e34eec9a類其實就是UserService的代理類

invokeSuper結論

所以當我們執行invokeSuper方法的時候,不能傳入原始類(UserService)只能傳入代理類對象,不然就無法轉換成爲代理類類型

所以FastClass 快的地方就是預先把所有的方法信息都生成了對應的index,在真正的去執行的時候不用再去找Method對象,直接傳入對應方法的index就可以直接執行對應的方法了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章