首頁>技術>

先看下測試的原始碼:

CGLIB動態代理測試程式碼

輸出:

inceptor-1---before invoke method: sayHello i am cglibinceptor-1---after invoke method: sayHello

我們看下CGLIB生成的代理類(精簡之後)

生成的代理類(子類)

透過註釋我們可以觀察到代理類以及代理物件的實現方式:

首先CGLIB使用ASM工具根據父類HelloServiceImpl.class及我們定義的方法攔截器MethodInterceptor生成代理CglibTest$HelloServiceImpl$$EnhancerByCGLIB$$69dcec54的位元組碼,然後將該位元組碼載入到虛擬機器並根據靜態程式碼塊使用INIT方法初始化類中的類變數parentSayHelloMethod、sayHelloMethodProxy(及其他資訊,截圖中已省略其他資訊,防干擾)例項化代理類CglibTest$HelloServiceImpl$$EnhancerByCGLIB$$69dcec54的物件,記為代理物件A,並伴隨著代理類中的成員屬性methodInterceptor初始化為我們定義的MethodInterceptor物件,因為我們的代理類繼承了HelloServiceImpl,因此可以強轉(HelloServiceImpl)A呼叫代理物件的sayHello方法:(HelloServiceImpl)A.sayHello(...)結合上圖,我們看到呼叫sayHello實際上呼叫了我們定義的MethodInterceptor的methodInterceptor方法

methodInterceptor方法

接下來我們關注紅框中的方法:

methodProxy.invokeSuper(proxyObj, args); // 7、執行父類對應方法

methodProxy這個物件是在生成的代理類的INIT方法中進行初始化的(上去看下圖):

sayHelloMethodProxy = MethodProxy.create(parentClass, proxyClass, "方法描述", "sayHello", "cglibSayHello");

我們看下create方法:

建立methodProxy物件

接下來看invokeSuper方法:

invokeSuper方法

在這個方法中,init方法用來初始化fastClassInfo這個屬性物件,最終呼叫的是fci.f2.invoke方法。根據名字我們推斷fastClassInfo是快速的,應該是用來最佳化效能的一個東西,我們回顧JDK動態代理的實現方式時,我們在invoke方法中是根據method物件反射進行呼叫的,我們知道反射的效能相對較低,那麼fastClassInfo這個物件是怎麼最佳化的呢?

我們來看下init方法,為了說明init方法的意思,寫了兩個類(真實的這兩個類也是CGLIB動態生成的,內部為每個方法描述生成了唯一的索引並使用switch表示式進行分支控制,本例為了簡單說明是使用if語句根據方法名稱進行判斷,理解意思即可

class HelloServiceImpl$$FastClass{    public Object invoke(int index, HelloServiceImpl obj, Object[] args){        if (index == "sayHello".hashCode()){            return obj.sayHello((String)args[0]);        }else {            // ....其他方法        }        return null;    }}class Proxy$$FastClass{    public Object invoke(int index,  ProxyClass proxy, Object[] args){        if (index == "cglibSayHello".hashCode()){            return proxy.cglibSayHello((String)args[0]);        }else {            // ....其他方法        }        return null;    }}
CreateInfo ci = createInfo;FastClassInfo fci = new FastClassInfo();fci.f1 = helper(ci, ci.c1); // f1指向HelloServiceImpl$$FastClass的一個例項fci.f2 = helper(ci, ci.c2); // f2指向Proxy$$FastClass的一個例項fci.i1 = fci.f1.getIndex(sig1); // 生成父類sayHello方法的一個唯一索引fci.i2 = fci.f2.getIndex(sig2); // 生成代理類cglibSayHello方法的一個唯一索引fastClassInfo = fci;createInfo = null;

完成上面的初始化後我們進入到這個方法的分析:

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

f2指向Proxy$$FastClass例項(上面的註釋),呼叫的就是proxy.cglibSayHello((String)args[0])這個方法,我們再回到最開始生成的代理類中,檢視cglibSayHello方法,呼叫的是父類的sayHello方法:

final String cglibSayHello(String var1) {    return super.sayHello(var1);}

這樣就形成了一個閉環~~~~

因此CGLIB在這一部分的最佳化就是用了判斷的形式直接呼叫的物件的方法,付出的代價是多載入了幾個類到虛擬機器中,是一種空間換時間的一種思想;而JDK的方式是使用方法物件進行反射呼叫,節省了空間但降低了效率

總結JDK動態代理和CGLIB動態代理:(兩種實現方式大體思路基本相同)

JDK動態代理:

需要目標類實現介面生成的代理類是與目標類平級,實現了共同的介面使用反射的方式進行最終方法的呼叫,效能較低

CGLIB動態代理:

9
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • canal同步elasticsearch實戰(二)