Java動態(tài)代理

代理模式

解說:給某一個對象提供一個代理,并由代理對象控制對原對象的引用;

代理模式需要以下幾個角色:

1 ?主題:規(guī)定代理類和真實對象共同對外暴露的接口;

2 ?代理類:專門代理真實對象的類;

3 ?真實對象:需要被代理的對象;

代理解決的主要的業(yè)務就是需要在 真實對象的某個接口 前后處理一些事情,框架中多會用到這種功能,比如 打日志、記錄時間等

靜態(tài)代理

靜態(tài)代理是指自己動手編寫代碼實現(xiàn)代理類;

優(yōu)點:業(yè)務類只需要關注業(yè)務邏輯本身,保證了業(yè)務類的重用性。這是代理的共有優(yōu)點。

缺點:每一個真實對象都需要一個具體的代理類,不能做到可重用;

靜態(tài)代理比較簡單,下邊用代碼來具體說明;

主題接口:IAnimal

IAnimal

真實對象:Dog

dog

代理:DogProxy

dogproxy

靜態(tài)代理使用:

代理和真實對象對外暴露一致

動態(tài)代理

動態(tài)代理是指在運行時動態(tài)生成代理類;

jdk

要使用Java中原生的動態(tài)代理,需要用到以下幾個類和接口

接口InvocationHandler

Proxy類

我們還是用靜態(tài)代理用到的代碼:主題接口IAnimal和真實對象Dog不變,去掉DogProxy和StaticProxyTest,增加以下代碼

DogProxyInvocationHandler

DynamicProxyTest

可以看出:Java動態(tài)代理 我們必須有真實對象,實現(xiàn)了InvocationHandler接口的自己的處理類,然后通過Proxy生成代理類

輸出如下:

這里動態(tài)代理的優(yōu)勢相比靜態(tài)代理為:即使真實對象有N個接口,我們的invocationHandler只需要一個Invoke方法即可!

這里有幾個問題:

1 ?動態(tài)代理生成的class name為什么是 $Proxy0 ?

相關變量如下:

以上便可以解決相關問題

2 ?動態(tài)代理生成的代理類到底是什么樣子的?生成代理類的關鍵接口是什么?為何調用真實對象的某個接口會進入invoke方法?

通過分析源碼:我們知道 Proxy.newProxyInstance?→ Proxy.getProxyClass0?→ WeakCache.get?→ WeakCache.Factory.get?→ Proxy.ProxyClassFactory.apply?→ ProxyGenerator.generateProxyClass

最終生成了一個 byte[] 類型的 class類;這樣byte[] 比較抽象 ,我們想看到該怎么辦?可以通過下邊的代碼生成Proxy0

生成的代碼如下

以上代碼為我們解答了紅色的問題。雖然代碼是這樣的? 代理類集成了 Proxy類,但是如果想要驗證 如何驗證呢?

cglib

cglib是什么?CGLIB is a powerful, high performance code generation library.

特點簡單說:

CGLib (Code Generation Library) 是一個強大的,高性能,高質量的Code生成類庫;

它可以在運行期擴展Java類與實現(xiàn)Java接口;

CGLib 比 Java 的 java.lang.reflect.Proxy 類更強的在于它不僅可以接管接口類的方法,還可以接管普通類的方法。

CGLib 的底層是Java字節(jié)碼操作框架 —— ASM

引入JAR包支持,如下:

目前最新的版本是 3.2.4,CGLib的package分布和作用如下:

net.sf.cglib.core:底層字節(jié)碼處理類,他們大部分與ASM有關系,對其進行封裝,更易于使用;

net.sf.cglib.transform:編譯期或運行期類和類文件的轉換;

net.sf.cglib.proxy:實現(xiàn)創(chuàng)建代理和方法攔截器的類;

net.sf.cglib.reflect:實現(xiàn)快速反射的類;

net.sf.cglib.util:集合排序工具類;

net.sf.cglib.beans:JavaBean相關的工具類;

我們沿用上邊的例子,來做下演示:

加入你有一個類Animal,打算對里邊的所有方法進行包裝,由于這個類沒有實現(xiàn)接口,所以你無法使用jdk 動態(tài)代理

你現(xiàn)在想要在testt方法輸出前后不加任何內容,但是另外兩個方法輸出前后要加一個字符串,效果如下:

其中=======是分隔線,上邊兩個方法前后都改變了,但是最后一個方法則沒做任何改變還是原生的。如何做到?

首先我們要定義一個攔截器,該攔截器實現(xiàn)了 cgLib的MethodInterceptor,如下:

再定義一個攔截器過濾器,如下:

接下來看下測試類:

CGLib通過 Enhancer、Callback、CallbackFilter就可以實現(xiàn)上述功能了。

深入代碼:Enhancer?→ KeyFactory.Generator?→ AbstractClassGenerator?→ DefaultGeneratorStrategy.generate?→ KeyFactory.Generator.generateClass?→ ClassWriter.toByteArray 生成 class bytecode .

生成的代碼反編譯如下:

附博客:動態(tài)代理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容