Java的動態(tài)代理模式之Cglib代理詳解
1.Cglib 代理模式的基本介紹
- 靜態(tài)代理和 JDK 代理模式都要求目標對象是實現(xiàn)一個接口,但是有時候目標對象只是一個 單獨的對象,并沒 有實現(xiàn)任何的接口,這個時候可使用目標對象子類來實現(xiàn)代理-這就是 Cglib 代理
- Cglib代理也叫作 子類代理,它是在內(nèi)存中構(gòu)建一個子類對象從而實現(xiàn)對目標對象功能擴展, 有些書也將Cglib代理歸屬到動態(tài)代理。
- Cglib 是一個強大的高性能的代碼生成包,它可以在運行期擴展 java 類與實現(xiàn) java 接口.它廣泛的被許多 AOP 的框架使用,例如 SpringAOP,實現(xiàn)方法攔截
- 在 AOP 編程中如何選擇代理模式:
目標對象需要實現(xiàn)接口,用 JDK 代理
目標對象不需要實現(xiàn)接口,用 Cglib 代理
Cglib 包的底層是通過使用字節(jié)碼處理框架 ASM 來轉(zhuǎn)換字節(jié)碼并生成新的類
2.Cglib 代理模式實現(xiàn)步驟
需要引入 cglib 的 jar 文件
/* cglib包結(jié)構(gòu): net.sf.cglib.core 底層字節(jié)碼處理類。 net.sf.cglib.transform 該包中的類用于class文件運行時轉(zhuǎn)換或編譯時轉(zhuǎn)換。 net.sf.cglib.proxy 該包中的類用于創(chuàng)建代理和方法攔截。 net.sf.cglib.reflect 該包中的類用于快速反射,并提供了C#風(fēng)格的委托。 net.sf.cglib.util 集合排序工具類。 net.sf.cglib.beans JavaBean工具類。 */
在內(nèi)存中動態(tài)構(gòu)建子類,注意代理的類不能為 final,否則報錯java.lang.IllegalArgumentException:
目標對象的方法如果為 final/static,那么就不會被攔截,即不會執(zhí)行目標對象額外的業(yè)務(wù)方法.
3.cglib動態(tài)代理相關(guān)的基礎(chǔ)類
net.sf.cglib.proxy.Enhancer 主要的增強類。
net.sf.cglib.proxy.MethodInterceptor 主要的方法攔截類,它是Callback接口的子接口,需要用戶實現(xiàn)。
net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method類的代理類,可以方便的實現(xiàn)對源對象方法的調(diào)用。
cglib是通過動態(tài)的生成一個子類去覆蓋所要代理類的非final方法,并設(shè)置好callback,則原有類的每個方法調(diào)用就會轉(zhuǎn)變成調(diào)用用戶定義的攔截方法(intercept)
4.Cglib 代理模式應(yīng)用實例
4.1 導(dǎo)入pom.xml依賴
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
4.2 目標類
//火車站 public class TrainStation { public void sell() { System.out.println("火車站賣票"); } }
4.3 代理工廠
//代理工廠 public class ProxyFactory implements MethodInterceptor { //聲明目標對象 private TrainStation target = new TrainStation(); public TrainStation getProxyObject() { //創(chuàng)建Enhancer對象,類似于JDK動態(tài)代理的Proxy類,下一步就是設(shè)置幾個參數(shù) Enhancer enhancer =new Enhancer(); //設(shè)置代理對象的父類的字節(jié)碼對象(Class類型的對象) , 指定代理對象的父類 enhancer.setSuperclass(target.getClass()); //設(shè)置回調(diào)函數(shù) , 實現(xiàn)調(diào)用代理對象的方法時最終都會執(zhí)行MethodInterceptor的子實現(xiàn)類的intercept方法 , 在這個函數(shù)中利用反射完成任意目標類方法的調(diào)用 enhancer.setCallback(this); //設(shè)置完參數(shù)后就可以 ,默認返回的是Object類型 , 可以進行強轉(zhuǎn) , 創(chuàng)建真正的代理對象 TrainStation obj = (TrainStation) enhancer.create(); return obj; } // intercept方法參數(shù)說明: // 返回值類型是調(diào)用方法的返回值類型 // o : 代理對象 // method : 真實對象中的方法的Method實例對象 // args : 實際參數(shù) , 可以是0到N個 // methodProxy :代理對象中的方法的method實例 public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("代理點收取一些服務(wù)費用(CGLIB動態(tài)代理方式)"); //調(diào)用目標對象的方法 TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args); return result; } }
Enhancer類的常用方法
方法名 | 功能 |
public Enhancer() , 無參構(gòu)造 | 創(chuàng)建Enhancer對象,類似于JDK動態(tài)代理的Proxy類,并不是真正的代理對象 , 還沒有設(shè)置參數(shù) |
setSuperclass() | 設(shè)置代理對象的父類的字節(jié)碼對象(Class類型的對象) , 指定代理對象的父類 , 參數(shù)一般寫目標類的Class對象 |
setCallback() | 設(shè)置執(zhí)行哪個對象的回調(diào)函數(shù) , 調(diào)用代理對象的方法時最終都會執(zhí)行MethodInterceptor接口的子實現(xiàn)類對象的intercept方法 , 在intercept方法中利用反射完成任意目標類方法的調(diào)用 一般讓代理工廠實現(xiàn)MethodInterceptor接口 , 那么方法參數(shù)就可以寫this,這時MethodInterceptor接口的子實現(xiàn)類對象就是代理工廠對象 |
public Object create() | 創(chuàng)建真正的代理對象 , 默認返回的是Object類型 , 可以強轉(zhuǎn)為目標對象類型 , 創(chuàng)建真正的代理對象 |
4.4 測試類
//測試類 public class Client { public static void main(String[] args) { //創(chuàng)建代理工廠對象 ProxyFactory factory = new ProxyFactory(); //獲取代理對象 TrainStation proxyObject = factory.getProxyObject(); proxyObject.sell(); } }
5 jdk代理和CGLIB代理
- 使用CGLib實現(xiàn)動態(tài)代理,CGLib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類,在JDK1.6之前比使用Java反射效率要高。
- 唯一需要注意的是,CGLib不能對聲明為final的類或者方法進行代理,因為CGLib原理是動態(tài)生成被代理類的子類。final修飾類不能被繼承 , final修飾的方法不能被重寫
- 在JDK1.6、JDK1.7、JDK1.8逐步對JDK動態(tài)代理優(yōu)化之后,在調(diào)用次數(shù)較少的情況下,JDK代理效率高于CGLib代理效率,只有當進行大量調(diào)用的時候,JDK1.6和JDK1.7比CGLib代理效率低一點,但是到JDK1.8的時候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK動態(tài)代理,如果沒有接口使用CGLIB代理。
6 代理模式優(yōu)缺點及使用場景
優(yōu)點:
- 代理模式在客戶端與目標對象之間起到一個中介作用和保護目標對象的作用;
- 代理對象可以擴展目標對象的功能;
- 代理模式能將客戶端與目標對象分離,在一定程度上降低了系統(tǒng)的耦合度;
缺點:
- 增加了系統(tǒng)的復(fù)雜度;
使用場景:
- 遠程(Remote)代理
- 本地服務(wù)通過網(wǎng)絡(luò)請求遠程服務(wù)。為了實現(xiàn)本地到遠程的通信,我們需要實現(xiàn)網(wǎng)絡(luò)通信,處理其中可能的異常。為良好的代碼設(shè)計和可維護性,我們將網(wǎng)絡(luò)通信部分隱藏起來,只暴露給本地服務(wù)一個接口,通過該接口即可訪問遠程服務(wù)提供的功能,而不必過多關(guān)心通信部分的細節(jié)。RPC思想
- 防火墻(Firewall)代理
- 當你將瀏覽器配置成使用代理功能時,防火墻就將你的瀏覽器的請求轉(zhuǎn)給互聯(lián)網(wǎng);當互聯(lián)網(wǎng)返回響應(yīng)時,代理服務(wù)器再把它轉(zhuǎn)給你的瀏覽器。
- 保護(Protect or Access)代理
- 務(wù)一個接口,通過該接口即可訪問遠程服務(wù)提供的功能,而不必過多關(guān)心通信部分的細節(jié)。RPC思想
- 防火墻(Firewall)代理
- 當你將瀏覽器配置成使用代理功能時,防火墻就將你的瀏覽器的請求轉(zhuǎn)給互聯(lián)網(wǎng);當互聯(lián)網(wǎng)返回響應(yīng)時,代理服務(wù)器再把它轉(zhuǎn)給你的瀏覽器。
- 保護(Protect or Access)代理
- 控制對一個對象的訪問,如果需要,可以給不同的用戶提供不同級別的使用權(quán)限。
到此這篇關(guān)于Java的動態(tài)代理模式之Cglib代理詳解的文章就介紹到這了,更多相關(guān)Cglib代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)雪花算法的原理和實戰(zhàn)教程
這篇文章主要介紹了Java實現(xiàn)雪花算法的原理和實戰(zhàn)教程,本文通過語言表述和代碼的實現(xiàn)講解了該項算法,,需要的朋友可以參考下2021-06-06一文詳解SpringBoot?Redis多數(shù)據(jù)源配置
Spring?Boot默認只允許一種?Redis?連接池配置,且配置受限于?Lettuce?包,不夠靈活,所以本文將為大家介紹如何自定義Redis配置方案實現(xiàn)多數(shù)據(jù)源支持,需要的可以參考下2024-11-11解決MyEclipse出現(xiàn)the user operation is waiting的問題
今天做項目的時候每次修改代碼保存后都會跳出一個框框,然后就有兩個進度條,上面寫the user operation is wating...小編去網(wǎng)上查了查解決了這個問題,下面跟大家分享一下。2018-04-04Java JDBC批量執(zhí)行executeBatch方法詳解
這篇文章主要介紹了Java JDBC批量執(zhí)行executeBatch方法詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫連接池操作
這篇文章主要介紹了SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫連接池操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09