9、cglib demo分析及びmethodProxyとFastclassソースコード


前言
前節ではsayメソッドが最終的に転送され、demoではcglib.CglibProxy#interceptこれは
Object result = methodProxy.invokeSuper(o, objects);

このinvokeSuperは何ですか?エージェントクラス関数の呼び出しを親クラス対応関数の呼び出しに転送する方法.
ここではmethodProxyおよびFastClassメカニズムについて説明します.この2つも密接に関連している.
ソース分析
メソッドエージェントの作成CGLIB$methodName$index$Proxy
まず直接結論***メソッドエージェントの役割はclass 1を示すことである.method 1メソッドに対応するエージェントメソッドはclass 2である.method2 ***
コードを見てcglib.CglibProxy#intercept前のエージェントクラスclass逆コンパイルファイルで渡されるパラメータ
Object object = methodInterceptor.intercept((Object)this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);

//CGLIB$say$0$Proxy   
CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//  
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

上のコードでは、MethodProxy#createはパラメータに基づいて「署名」を行います.
  A representation of a method signature, containing the method name,
  return type, and parameter types.

署名には、メソッド名、戻りタイプ、およびパラメータタイプが含まれます.ソースコードは次のとおりです.
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;
    }

***という一節の意味は,クラスc 1を依頼するメソッドname 1であり,対応するエージェントメソッドはクラスc 2を実現するメソッドname 2***である.
メソッドエージェントの呼び出し
またnet.sf.cglib.proxy.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();
        }
    }

//init      
    private void init()
    {
        /*
              
         */
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    /*
                      fastClass f1,f2,  class    
                     */
//                    System.out.println("net.sf.cglib.proxy.MethodProxy.helper " + ci.c1.getName() + "  " + ci.c2.getName() + " " + this.sig1 + " " + this.sig2);
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    /*
                           f1,f2    ,    index(     ,index        )
                        index          
                     */
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

//helper      
        private static FastClass helper(CreateInfo ci, Class type) {
        //  ci         ,   ,         fastClass
        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呼び出しで作成
直接参考できるhttp://www.cnblogs.com/cruze/p/3865180.htmlここでいう比較的わかりやすい主な考え方は,メソッド呼び出し時に反射を過度に用いて呼び出しが遅いという問題を回避するために,各メソッドに1つの署名を与えることであり,この署名に遭遇した場合には呼び出し実装クラスの実装方法を直接表示し,以下のfastClassファイルを参考にすることができる.
fastClassの作成
fastClassの作成タイミング
demoから見るとfastClassはこのような順序で作成されています
net.sf.cglib.proxy.MethodProxy#invokeSuper
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create

***ここではlazy initという考え方があります***つまり、最初はfastClassではなくMethodProxyのみでしたが、メソッドエージェントが本当に要求されたときにclass 1のmethod 1メソッドがclass 2のmethod 2メソッドにマッピングされる必要がある場合、反射を過度に使用しないようにfastClassの便利な呼び出しを生成して分かりやすくするために、
fastClassの作成は何回かあります
すなわち、いくつかのfastClassクラスのclassファイル***が生成する、f 1とf 2(fast(c 1)とfast(c 2)と理解する)***net.sf.cglib.proxy.MethodProxy#initでは2回netを呼び出しました.sf.cglib.proxy.MethodProxy#helperこの2つのファイルの違いは何ですか?
オブジェクト
インスタンス
c1
cglib.CglibLearn$serviceImpl
f1
c 1のfastClassは、c 1の関数cglibを呼び出すのに便利である.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381
c2
cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b 2 e 6 ff 51すなわちc 1のenhance class
f2
c 2のfastClassは、c 2の関数を呼び出すのに便利です.cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902
f 1,f 2はいずれも従来のc 1,c 2に対してmethod->signature->indexのマッピングを行った
fastClassクラスの内容はどのようにgenerateが出てきたのか
net.sf.cglib.reflect.FastClass.Generator#generateClassここでは詳しく説明しませんが、前節Enhancer#generateClassがやったことと同じように、より難易度が低く、下位層はasm生成を利用しています.
fastClassクラスの内容は何ですか?
f 1を例として(f 2の後に一部をスクリーニング)、すなわちcglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733 f 381を例にとると、class逆コンパイルのコードは以下の通りである.
package cglib;
import cglib.CglibLearn.serviceImpl;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

/* compiled from:  */
public class CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381 extends FastClass {
    public CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381(Class cls) {
        super(cls);
    }

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

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

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

    public int getMaxIndex() {
        return 9;
    }

    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
            case 1:
                cglib_CglibLearn_serviceImpl.wait();
                return null;
            case 2:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue(), ((Number) objArr[1]).intValue());
                return null;
            case 3:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue());
                return null;
            case 4:
                return new Boolean(cglib_CglibLearn_serviceImpl.equals(objArr[0]));
            case 5:
                return cglib_CglibLearn_serviceImpl.toString();
            case 6:
                return new Integer(cglib_CglibLearn_serviceImpl.hashCode());
            case 7:
                return cglib_CglibLearn_serviceImpl.getClass();
            case 8:
                cglib_CglibLearn_serviceImpl.notify();
                return null;
            case 9:
                cglib_CglibLearn_serviceImpl.notifyAll();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
        invocationTargetException = new InvocationTargetException(th);
    }

    public Object newInstance(int i, Object[] objArr) throws InvocationTargetException {
        switch (i) {
            case 0:
                try {
                    return new serviceImpl();
                } catch (Throwable th) {
                    InvocationTargetException invocationTargetException = new InvocationTargetException(th);
                }
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
}

上のコードを理解するには、親fastClassのいくつかの関数定義を見てみましょう.
//       hashCode,    method   index
net.sf.cglib.reflect.FastClass#getIndex(net.sf.cglib.core.Signature)

//  method   index,          
net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])

***なぜfastClassが反射より速いと言うのか***net.sf.cglib.reflect.FastClass#invoke(int,java.lang.Object,java.lang.Object[])関数では、上記のように実装クラスオブジェクトが直接作成され、識別子indexでswitch caseに行って対応呼び出しが行われます.
    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
//  

結果分析
上のinvokeSuperは戻ります
fci.f2.invoke(fci.i2, obj, args)

メソッドエージェント(そうでなければf 1、ここではf 2)で、lazy initに記録されたメソッド署名対応のフラグi 2を渡し、f 2に対応する処理をさせる
上のfastclassはf 1のもので、ここではf 2の対応する部分だけが貼られていて、f 2は比較的長いです
クラス名cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902
    public int getIndex(Signature signature) {
        String obj = signature.toString();
        switch (obj.hashCode()) {
            case 1540695073:
                if (obj.equals("CGLIB$say$0()V")) {
                    return 17;
                }
                break;
         }
     }

     public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        b2e6ff51 cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51 = (b2e6ff51) obj;
        switch (i) {
            case 17:
                cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }


//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()  
    final void CGLIB$say$0() {
        super.say();// CglibLearn.serviceImpl.say()  
    }

ここでc 1が完成する.sayからc 2.CGLIB$say$0()の転送は、なぜc 1ではないのか疑問に思うかもしれません.sayからc 2.sayの転送これはc 2で定義されているためです(前節にもこのコードがあり、本節では最も)
CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//  
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

 Object say         serviceImpl$$EnhancerByCGLIB$$4e65f4b  CGLIB$say$0  

すなわち,c 1のsayメソッドがc 2のCGLIB$say$0メソッドに転送されることを示した.
考える
1.fastclassが反射よりも速い理由は、メソッドの前または識別子indexによって、switch caseによって直接オブジェクトを利用して関数を呼び出す反射がjavaである.lang.reflect.Method#invoke、少し複雑で、これは具体的な実現を研究したことがありません
2.MethodProxy#invokeとMethodProxy#invokeSuperの違い、すなわち[c 1,f 1]と[c 2,f 2]の違い[c 1,f 1]に対応する親のclassとfastclass[c 2,f 2]に対応する親の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ファイルは1回(method呼び出しではなく1回)しか生成されず、1回でクラス全体の情報があり、このmethod関連情報だけではないのでしょうか.
一つ目:同じクラスのfastClassは一度しか生成されません.netです.sf.cglib.reflect.FastClass.Generator#create net.sf.cglib.core.AbstraactClassGenerator#createにキャッシュが使われています
2つ目は、このmethod情報netだけではなく、クラス全体の情報が一度に存在することです.sf.cglib.proxy.MethodProxy#create時にclass c 1に転送され、c 2がfastClassを作成するとnet.sf.cglib.proxy.MethodProxy#helperはg.setType(type)を呼び出した.fastClass生成時net.sf.cglib.reflect.FastClass.Generator#generateClassは、これまでに設定されたClass type、つまりクラス情報がinvokeSuperになるまでのロジックを使用します.
4.invokeSuperをinvokeに変更するとどうなるか結論:デッドサイクル、スタックオーバーフロー原因分析:MethodProxyメソッドエージェントに相当し、エージェントはc 1を見ていない.sayメソッドは、前節で挙げました
final void say() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            methodInterceptor.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
        } else {
            say();
        }
    }

ここから行くinterceptは、aop本来invokeSuperを呼び出したことがありますが、ここでinvokeに変更すると、
    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;
        }
    }

f 1(以前invokeSuper用のf 2)を用い,f 1のコードは上記の対応ロジックを参照する.
//getIndex say      0  index

public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }

つまりサービスインパルスですsay()はinvoke(非invokeSuper)を通じて結局serviceImplに戻った.say()は,再帰的にスタックオーバーフローをもたらす.
5.methodProxyとfastClassを組み合わせてmethodProxyを使用してメソッドエージェントを生成する関係バインド(classA.methodAはclassB.methodBエージェント)fastClassを使用してメソッドエージェントの迅速な呼び出しを完了し、署名によって識別indexを取得し、重複反射を回避する
突っ込む
1.methodProxyはfastClassの生成を担当しているが、methodProxyはfastClassの生成を複数回呼び出し、fastClassが最終的に1部のclassファイルであるエージェントメソッドを呼び出す場合にfastEnhanceクラスを作成し、過去のlazyの思想感覚を転送するのは自分の職責を超えている
2.c 1,c 2,f 1,f 2の関係が少し絡んでいる
3.文書数が少ない
4.逆コンパイル用http://www.javadecompilers.com/CFR(very good and well-supported decompiler for Java 8)Jadx、fast and with Android supportを選択しないでください.そうしないとコードが誤解されます.例えば、
//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()           

//CFR (very good and well-supported decompiler for Java 8)  

    final void CGLIB$say$0() {
        super.say();// CglibLearn.serviceImpl.say()  
    }

//Jadx, fast and with Android support
    final void CGLIB$say$0() {
        say();//super  !!!    invokeSuper          !!!
    }

refer
http://www.cnblogs.com/cruze/p/3865180.html
転記先:https://www.jianshu.com/p/001f866a49d7