cglib淺析
cglib實作動态代理,并不要求被代理類必須實作接口,底層采用asm位元組碼生成架構生成代理類位元組碼(該代理類繼承了被代理類)。
是以被代理類一定不能定義為<code>final class</code>并且對于<code>final</code> 方法不能被代理。
實作需要
導入依賴
userdaoimpl 使用者實作類(realsubject)
cglibproxy cglib代理類(proxy)
proxyfactory 代理工廠
建立代理對象會經過三步:
1.生成代理類的二進制位元組碼檔案。
2.加載二進制位元組碼檔案到jvm,生成class對象。
3.反射獲得執行個體構造方法,建立代理對象。
接下來,看看反編譯出現的<code>java檔案</code>:
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>方法,繼而是一直死循環。