代理模式分類
一、代理概念
代理的兩種模式靜態(tài)代理和動態(tài)代理,這個是怎么區(qū)別的我們談一下,Java中都是通過編譯器生成.class文件,在通過JVM讀取,
然后加載到內存中,生成對應的需要對象,根據(jù)代理類創(chuàng)建的時間分成靜態(tài)和動態(tài)代理,靜態(tài)的代理就是在程序運行前.class文件就存在,
我們經常使用的代理模式就是靜態(tài)代理,動態(tài)代理就是程序運行時動態(tài)創(chuàng)建,比如JDK的動態(tài)代理和CGLB代理;
二、靜態(tài)代理----代理模式
這里我們使用取款機這個例子來說一下,現(xiàn)在假如說你是招商的銀行卡,你需要在工商取款機取款,這里有個規(guī)定那就是每筆需要收費2元,這個工商取款機就是代理對象,你的目標對象就是取款,我們抽象一個取款的接口WithdrawService,招商取款機上面的實現(xiàn)取款機接口ATM,然后工商取款機ATMProxy實現(xiàn)代理角色;當然這里我簡化很多步驟;
1 /** 2 * 抽象的取款接口 3 */ 4 public interface WithdrawService { 5 int GetByMoneyWithdraw(int money); 6 } 7 8 /** 9 * ATM機實現(xiàn)的接口 10 */ 11 public class ATM implements WithdrawService{ 12 @Override 13 public int GetByMoneyWithdraw(int money) { 14 System.out.print("取款"+money); 15 return money; 16 } 17 } 18 19 /** 20 * 代理對象 21 */ 22 public class ATMProxy implements WithdrawService { 23 private ATM atm; 24 public ATMProxy(ATM atm){ 25 this.atm=atm; 26 } 27 @Override 28 public int GetByMoneyWithdraw(int money) { 29 30 int proxyMoney=2; 31 System.out.print("取款"+money+":"+"手續(xù)費"+proxyMoney); 32 atm.GetByMoneyWithdraw(money+proxyMoney); 33 return money+proxyMoney; 34 } 35 } 36 /** 37 * 測試類 38 */ 39 public class ProxyTest { 40 public static void main(String[] args){ 41 WithdrawService withdrawService=new ATMProxy(new ATM()); 42 withdrawService.GetByMoneyWithdraw(1000); 43 } 44 }
以上就是靜態(tài)代理,在運行前就生成,代理模式的優(yōu)點就在于不直接依賴于目標對象,而是通過代理對象作為中間對象也就是我們經常說的解耦;另外代理對象還可以對實現(xiàn)的方法進行增強,增加自己的規(guī)則,這里我們思考一個問題:當我們每增加一個代理類的時候,我就需要編寫一個類,這樣我們代碼通用很差,另外這樣子會照成系統(tǒng)更加復雜,執(zhí)行速度更加慢;這就背離了我們設計的原則,那么怎么避免這種問題?
三、動態(tài)代理----JDK動態(tài)代理
針對于上面提出的問題,通過反射機制,JDK給我們提供Proxy類,這個只是針對于接口的增強,在JVM運行時動態(tài)創(chuàng)建生成。這里簡單說一下JDK通過接口動態(tài)生成代理對象的過程:
1.獲取WithdrawService接口下所有方法;
2.生成代理類,默認為命名空間+$+Proxy+類名;
3.根據(jù)接口,在代碼中動態(tài)創(chuàng)建代理的字節(jié)碼文件;
4.將字節(jié)碼文件轉化為class文件;
5.創(chuàng)建InvocationHandler實例handler,來處理代理的方法調用
6.Proxy的class對象以創(chuàng)建的handler對象為參數(shù),實例一個proxy對象;
下面我們展示一下JDK實現(xiàn)代理:
1 /** 2 * 抽象的取款接口 3 */ 4 public interface WithdrawService { 5 int GetByMoneyWithdraw(int money); 6 } 7 8 /** 9 * ATM機實現(xiàn)的接口 10 */ 11 public class ATM implements WithdrawService{ 12 @Override 13 public int GetByMoneyWithdraw(int money) { 14 System.out.print("取款"+money); 15 return money; 16 } 17 } 18 19 /** 20 * JDK動態(tài)代理的實現(xiàn) 21 */ 22 public class ATMJDKProxy implements InvocationHandler { 23 //目標對象 24 private Object target; 25 //構建目標對象 26 public ATMJDKProxy(Object target){ 27 super(); 28 this.target=target; 29 } 30 //獲取目標的代理對象 31 public Object getProxy(){ 32 return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),this.target.getClass().getInterfaces(),this); 33 } 34 @Override 35 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 36 System.out.print("執(zhí)行前處理什么事情"); 37 Object result = method.invoke(target, args); 38 System.out.print("執(zhí)行后處理什么事情"); 39 return result; 40 } 41 } 42 43 /** 44 * 測試類 45 */ 46 public class ProxyTest { 47 48 public static void main(String[] args) { 49 WithdrawService withdrawService=new ATM(); 50 ATMJDKProxy atmjdkProxy=new ATMJDKProxy(withdrawService); 51 WithdrawService proy= (WithdrawService) atmjdkProxy.getProxy(); 52 proy.GetByMoneyWithdraw(1000); 53 } 54 }
四、動態(tài)代理----CGLIB動態(tài)代理
JDK動態(tài)代理只能實現(xiàn)對接口方法的增強,不能實現(xiàn)對接口類的的動態(tài)代理。那么我們想動態(tài)生成類的時候怎么辦,不用想了那就是使用CGLIB類庫;這個當然也是在JVM運行時動態(tài)創(chuàng)建,這里也簡單描述下生成ATM動態(tài)代理類的過程:
1.查找ATM類中的非final的public類型方法;
2.將這些方法定義轉化為字節(jié)碼;
3.將組成字節(jié)碼轉化為代理的Clss類;
4.實現(xiàn)MethodInterceptor 接口,用來處理代理方法上的請求;
下面我們展示一下CGLIB實現(xiàn)代理:
1 /** 2 * ATM不實現(xiàn)接口 3 */ 4 public class ATM { 5 public int GetByMoneyWithdraw(int money) { 6 System.out.print("取款"+money); 7 return money; 8 } 9 } 10 11 /** 12 * 實現(xiàn)MethodInterceptor 13 */ 14 public class ATMCGLIBProxy implements MethodInterceptor { 15 private Object target; 16 public Object getInstance(Object target){ 17 this.target=target; 18 Enhancer enhancer=new Enhancer(); 19 enhancer.setSuperclass(this.target.getClass()); 20 enhancer.setCallback(this); 21 return enhancer.create(); 22 } 23 @Override 24 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 25 System.out.println("開始前"); 26 methodProxy.invokeSuper(o,objects); 27 System.out.println("開始后"); 28 return null; 29 } 30 } 31 /** 32 * 測試類 33 */ 34 public class ProxyTest { 35 36 public static void main(String[] args) { 37 ATMCGLIBProxy cglib=new ATMCGLIBProxy(); 38 ATM atm = (ATM) cglib.getInstance(new ATM()); 39 atm.GetByMoneyWithdraw(1000); 40 } 41 }
以上是基本實現(xiàn),這里我們簡單說一下CGLIB底層是通過使用字節(jié)碼處理框架ASM,來轉換字節(jié)碼并生成新的類;什么是ASM?ASM是一個Java字節(jié)碼操縱框架,它能被用來動態(tài)生成類或者增強既有類的功能。ASM 可以直接產生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態(tài)改變類行為。Java class 被存儲在嚴格格式定義的 .class文件里,這些類文件擁有足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節(jié)碼(指令)。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。
這里需要注意使用這個的時候需要引入:cglib-nodep-2.2.jar;cglib-2.2.jar;asm-3.2jar;
五、動態(tài)代理----應用場景
大家執(zhí)行過上面的代碼以后會有一種是曾相識的感覺,在方法執(zhí)行前做一些事,在方法執(zhí)行后做一些事,使我們很容易想到Spring Aop中的通知,這里確實有用到動態(tài)代理,至于別的我暫時還沒有想到,等等那天解讀Spring源碼的時候我們再來探討一下,到此結束;
浙公網安備 33010602011771號