先看下測試的原始碼:
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動態代理: