天天看點

CGLib淺析

cglib淺析

cglib實作動态代理,并不要求被代理類必須實作接口,底層采用asm位元組碼生成架構生成代理類位元組碼(該代理類繼承了被代理類)。

是以被代理類一定不能定義為​<code>​final class​</code>​并且對于​<code>​final​</code>​ 方法不能被代理。

實作需要

導入依賴

userdaoimpl 使用者實作類(realsubject)

cglibproxy cglib代理類(proxy)

proxyfactory 代理工廠

CGLib淺析

建立代理對象會經過三步:

1.生成代理類的二進制位元組碼檔案。

2.加載二進制位元組碼檔案到jvm,生成class對象。

3.反射獲得執行個體構造方法,建立代理對象。

接下來,看看反編譯出現的​<code>​java檔案​</code>​:

CGLib淺析
cglib反編譯方法

使用以下語句

使用<code>hsdb</code>進行反編譯

使用 <code>authas</code> 配合 <code>jad</code>進行反編譯

具體使用方法可以自行查找

以​<code>​insert()​</code>​ 為入口開始:

這時候會進入​<code>​userdaoimpl$$enhancerbycglib$$f32f6ae2​</code>​ 中的 ​<code>​insert()​</code>​

其實在上述方法中,是因為設定了 ​<code>​enhancer.setcallback(cglibproxy);​</code>​ ,隻要不為空,則會執行

​<code>​this​</code>​ : 目前代理對象 ​<code>​cglib$say$0$method​</code>​ : 目标類中的方法 ​<code>​cglib$emptyargs​</code>​ : 方法參數,這裡為空 ​<code>​cglib$say$0$proxy​</code>​ : 代理類生成的代理方法

這樣會去調用 ​<code>​cglibproxy.intercept()​</code>​ 方法

這時候進入 ​<code>​methodproxy.invokesuper(o, objects)​</code>​ 方法

第一反應,可能不知道是​<code>​f2​</code>​、​<code>​i2​</code>​ 都是什麼,這裡扯一下 ​<code>​init()​</code>​ 方法,其中對于​<code>​fastclass​</code>​ 類,就是反編譯出來的對應類:

這時候看到​<code>​userdaoimpl$$enhancerbycglib$$f32f6ae2$$fastclassbycglib$$9fc87de5​</code>​ 中

是以 ​<code>​i2​</code>​ 在其中為 ​<code>​16​</code>​ , 這時候運作下面方法

即,​<code>​userdaoimpl$$enhancerbycglib$$f32f6ae2$$fastclassbycglib$$9fc87de5​</code>​ 中 ​<code>​invoke()​</code>​ 方法

可以看到,他進行調用的是 ​<code>​userdaoimpl$$enhancerbycglib$f32f6ae2​</code>​ 中的 ​<code>​cglib$insert$0()​</code>​ 方法

這裡,才是真正調用到了父類(目标類)中對應的方法。至此,整個的調用流程完畢。

首先生成代理對象。建立增強類enhancer,設定代理類的父類,設定回調攔截方法,傳回建立的代理對象;

調用代理類中的方法。這裡調用的代理類中的方法實際上是重寫的父類的攔截。重寫的方法中會去調用<code>intercept</code>方法;

調用intercept,方法中會對調用代理方法中的invokesuper方法。而在 <code>invokesuper</code> 中維護了一個 <code>fastclassinfo</code>類,其包含四個屬性字段,分别為<code>fastclass f1(目标類)</code>、<code>fastclass f2 (代理類)</code>、<code>int i1(目标類要執行方法的下标)</code>、<code>int i2(代理類要執行方法的下标)</code>; invokesuper中會調用的為代理類中的對應方法(代理類繼承于父類的時候,對于其父類的方法,自己會生成兩個方法,一個是重寫的方法,一個是代理生成的方法,這裡調用的即是代理生成的方法);

調用代理類中的代理方法。代理方法中通過<code>super.xxxx(string)</code>來實際真正的調用要執行的方法;

這時候,可能心中還有一個疑惑,明明下面兩個方法中都有 ​<code>​super.xxxx(string)​</code>​ , 但是使用的是 ​<code>​invokesuper()​</code>​ ,而不是 ​<code>​invoke()​</code>​

看下這兩個方法:

如果使用​<code>​invokesuper()​</code>​ :

就是按照上面講的步驟,先進行 ​<code>​insert()​</code>​ 方法,經過​<code>​intercept​</code>​,最終可以運作到 ​<code>​cglib$insert$0()​</code>​ ,調用到了父類(目标類)中對應的方法。

如果使用​<code>​invoke()​</code>​ :

​<code>​i1​</code>​ 的值通過下面方法擷取為 ​<code>​1​</code>​

接着,執行對應方法

先進行 ​<code>​insert()​</code>​ 方法,經過​<code>​intercept​</code>​,通過 ​<code>​invoke()​</code>​ 方法,再次進入​<code>​insert()​</code>​方法,繼而是一直死循環。