貌似停筆了近半個月了,實在不該啊,新的一年,時刻讓自己歸零。
Back To Zero,就從這篇文章拉開今年的序幕吧。
這篇文章準備介紹下有關(guān)代理模式的基本概念和靜態(tài)代理、動態(tài)代理的優(yōu)缺點及使用方法(包括擴展包CGLIB)
代理模式,又稱委托模式,顧名思義委托某物去辦某事。
舉個生活中的例子,臨近大年了,在外地學(xué)習(xí)工作的小伙伴們也都開始購買回家的火車票,先不說網(wǎng)上訂票,靠近火車站的小伙伴們自然很方便的可以到車站里去直接購票,那么如果是住郊外遠離火車站的小伙伴們那該怎么辦呢?當然也是可以專門搭車到火車站去買票,不過實為麻煩,這時候就衍生出一個東西——火車票代售處,沒錯,有了它,讓我們這些遠離火車站的小伙伴們也可以很輕松的購買到火車票。
這里的火車票代售其實就是一種代理(委托),火車票代售處代理了火車站售票,然而我們就可以在其中做一些我們想要的操作,比如權(quán)限的控制,代售處是不具備有退票的權(quán)限的。
好了,言歸正傳,先來看看代理的概念
1、代理概念為某個對象提供一個代理,以控制對這個對象的訪問。 代理類和被代理類有共同的父類或父接口,這樣在任何使用被代理類對象的地方都可以用代理對象替代。代理類負責(zé)請求的預(yù)處理、過濾、將請求分派給被代理類處理、以及被代理類執(zhí)行完請求后的后續(xù)處理。
來一張UML圖:

從圖中可以看出,代理接口(Subject)、代理類( 代理類和被代理類都同時實現(xiàn)了代理接口,然后代理類通過引用被代理類的對象,來處理用戶的訪問請求。 這邊根據(jù)代理類的生成時間又分成了2種代理,一種是靜態(tài)代理,一種是動態(tài)代理。 文縐縐的理論概念讓人看的難受,下面直接上實例,幫助大家理解。 2、靜態(tài)代理 由開發(fā)人員創(chuàng)建或某工具生成代理類的源碼,再編譯代理類。所謂靜態(tài)也就是在程序運行前就已經(jīng)存在代理類的字節(jié)碼文件,在代理類和被代理類運行前就確定了。 這里模擬一個計時器操作,記錄處理業(yè)務(wù)的時間,下面直接上代碼: 首先是代理接口,負責(zé)聲明業(yè)務(wù)方法,代理類和被代理類都必須實現(xiàn)它。 接下來是被代理類,這個類實現(xiàn)了代理接口,并執(zhí)行主要的業(yè)務(wù)邏輯操作。 再來是代理類,它同樣的實現(xiàn)了代理接口,這樣一來代理類和被代理類的對象既被"同化",通過構(gòu)造器可以對被代理類的對象進行引用,從而達到可以訪問被代理類的主要業(yè)務(wù)方法。 然后來個測試類。 看下運行效果: 到這里,靜態(tài)代理已經(jīng)實現(xiàn)了,先來看看靜態(tài)代理的優(yōu)點, 被代理類只需要去關(guān)注主要的業(yè)務(wù)實現(xiàn),其余操作比如日志記錄,計時,權(quán)限控制等都可以交給代理類去額外的處理。 不過,這樣子的實現(xiàn)方式,真的沒有問題嗎? 假如我們現(xiàn)在要為很多個業(yè)務(wù)方法都實現(xiàn)計時功能,如果這些方法分布在不同的類,那么是不是要為每一個類再單獨的去創(chuàng)建代理類,長久下來,類體積會膨脹會爆炸的。 我們退一步想,假如我們現(xiàn)在額外的要擴展業(yè)務(wù),在代理接口里添加新的業(yè)務(wù)方法,那么除了被代理類要去實現(xiàn)這個方法以外,是不是所有的代理類也都要去額外的添加實現(xiàn)這個方法,所謂的"牽一發(fā)而動全身",需要額外的添加大量的重復(fù)代碼,這是違背軟件設(shè)計原則的。 還有就是代理對象只服務(wù)于一種類型的對象,如果要服務(wù)多類型的對象。勢必要為每一種對象都進行代理,靜態(tài)代理在程序規(guī)模稍大時就無法勝任了。 為解決這個問題,我們的動態(tài)代理就要出場了。 3、JDK動態(tài)代理 JDK動態(tài)代理類的源碼是在程序運行期間由虛擬機JVM根據(jù)反射等機制動態(tài)的生成,所以不存在代理類的字節(jié)碼文件。在代理類和被代理類程序運行時確定。 在Java中要想實現(xiàn)動態(tài)代理機制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy類的支持。 來看下Java API里的主要方法介紹(具體自己去翻閱哈) java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 這是調(diào)用處理器接口,它自定義了一個 invoke 方法,用于集中處理在動態(tài)代理類對象上的方法調(diào)用,通常在該方法中實現(xiàn)對被代理類的代理訪問。 動態(tài)代理實現(xiàn)步驟 1、實現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器 2、給Proxy類提供ClassLoader和代理接口類型數(shù)組創(chuàng)建動態(tài)代理類 3、以調(diào)用處理器類型為參數(shù),利用反射機制得到動態(tài)代理類的構(gòu)造函數(shù) 4、以調(diào)用處理器對象為參數(shù),利用動態(tài)代理類的構(gòu)造函數(shù)創(chuàng)建動態(tài)代理類對象 好了,再說下去有的朋友要開始混亂了,直接看實例,整理下思路 依舊是上面的代理接口,這里我們需要創(chuàng)建一個自己的調(diào)用處理器(實現(xiàn)InvocationHandler接口) 接著是測試類 看下實現(xiàn)效果: 動態(tài)代理的優(yōu)缺點: 動態(tài)代理與靜態(tài)代理相比較,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數(shù)量比較多的時候,我們可以進行靈活處理,而不需要像靜態(tài)代理那樣每一個方法進行中轉(zhuǎn)。在本示例中看不出來,因為invoke方法體內(nèi)嵌入了具體的外圍業(yè)務(wù)(記錄任務(wù)處理前后時間并計算時間差),實際中可以類似Spring AOP那樣配置外圍業(yè)務(wù)。動態(tài)代理的應(yīng)用使我們的類職責(zé)更加單一,復(fù)用性更強。 美中不足: Proxy已經(jīng)設(shè)計得非常優(yōu)美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持 interface代理的桎梏,因為它的設(shè)計注定了這個遺憾。JDK動態(tài)代理只能為實現(xiàn)接口的類進行代理,沒有實現(xiàn)接口的類無法代理。 有問題就會想去解決,所以此時Cglib要登場了。 4、Cglib動態(tài)代理: Cglib的介紹: 代理為控制要訪問的目標對象提供了一種途徑。當訪問對象時,它引入了一個間接的層。JDK自從1.3版本開始,就引入了動態(tài)代理,并且經(jīng)常被用來動態(tài)地創(chuàng)建代理。JDK的動態(tài)代理用起來非常簡單,但它有一個限制,就是使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口。如果想代理沒有實現(xiàn)接口的繼承的類,該怎么辦?現(xiàn)在我們可以使用Cglib包。 Cglib是針對類來實現(xiàn)代理的,實現(xiàn)原理是為被代理類產(chǎn)生一個子類,通過攔截技術(shù)攔截父類方法的調(diào)用。 來看個小例子: 首先是一個業(yè)務(wù)邏輯類 再來一個Cglib代理類,提供代理對象,這里需要實現(xiàn)MethodInterceptor接口 寫個測試類試試 運行效果: 作者:Balla_兔子出處:http://m.survivalescaperooms.com/lichenwei/本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我! 1 package com.lcw.proxy.test; 2 3 /** 4 * 5 * 業(yè)務(wù)接口(代理接口) 6 * 7 */ 8 public interface ITask { 9 10 public void doTask(String taskName);11 12 } 1 package com.lcw.proxy.test; 2 3 import java.util.Random; 4 5 /** 6 * 7 * 業(yè)務(wù)處理類,處理主要的業(yè)務(wù)(被代理類) 8 * 9 */10 public class DealTask implements ITask {11 12 @Override13 public void doTask(String taskName) {14 try {15 System.out.println("執(zhí)行任務(wù):" + taskName);16 Thread.sleep(new Random().nextInt(1000));// 隨機休眠一段時間17 } catch (InterruptedException e) {18 e.printStackTrace();19 }20 }21 22 } 1 package com.lcw.proxy.test; 2 3 4 /** 5 * 6 * 代理類,需要實現(xiàn)和被代理類同一接口 7 * 利用構(gòu)造方法引用一個被代理類的對象為己持有 8 * 9 */10 public class ProxyTask implements ITask {11 12 private ITask dealTask;13 14 public ProxyTask(ITask dealTask){15 this.dealTask=dealTask;//引用一個被代理類的對象16 }17 18 @Override19 public void doTask(String taskName) {20 21 long startTime=System.currentTimeMillis();//記錄業(yè)務(wù)起始時間22 dealTask.doTask(taskName);//執(zhí)行被代理類的主要業(yè)務(wù)23 long endTime=System.currentTimeMillis();//記錄業(yè)務(wù)結(jié)束時間24 System.out.println("本次任務(wù)執(zhí)行時間為:"+(endTime-startTime+"毫秒"));25 26 }27 28 } 1 package com.lcw.proxy.test; 2 3 4 public class ProxyTest { 5 6 /** 7 * 測試類 8 */ 9 public static void main(String[] args) {10 //靜態(tài)代理11 ITask dealTask=new DealTask();//獲取被代理類的對象12 ITask proxyTask=new ProxyTask(dealTask);//將被代理類的對象注入,獲取靜態(tài)代理類對象13 proxyTask.doTask("打噴嚏..");14 15 }16 }



1 package com.lcw.proxy.test; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 /** 7 * 8 * 動態(tài)代理類 通過實現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器 9 * 10 */11 public class JDKProxy implements InvocationHandler {12 13 private Object dealTask;14 15 public JDKProxy(Object dealTask) {16 this.dealTask = dealTask;// 引用被代理類的對象17 }18 19 /**20 * 參數(shù): proxy為代理類的對象 method為被代理類的方法 args為該方法的參數(shù)數(shù)組21 * 22 */23 @Override24 public Object invoke(Object proxy, Method method, Object[] args)25 throws Throwable {26 // 調(diào)用實現(xiàn)業(yè)務(wù)邏輯方法,并實現(xiàn)附屬功能(日志,權(quán)限判斷等)27 // 利用反射機制將請求分派給被代理類處理28 29 long startTime = System.currentTimeMillis();// 記錄業(yè)務(wù)起始時間30 // invoke方法參數(shù)sub是實際的被代理對象,args為執(zhí)行被代理對象相應(yīng)操作所需的參數(shù)31 method.invoke(dealTask, args);// 返回值是一個Object對象也是調(diào)用被代理類方法的返回值(這里只是實現(xiàn)計時操作,所以返回值為null)32 long endTime = System.currentTimeMillis();// 記錄業(yè)務(wù)結(jié)束時間33 System.out.println("本次任務(wù)執(zhí)行時間為:" + (endTime - startTime + "毫秒"));34 return null;35 }36 37 } 1 package com.lcw.proxy.test; 2 3 import java.lang.reflect.Proxy; 4 5 public class ProxyTest { 6 7 /** 8 * 測試類 9 */10 public static void main(String[] args) {11 // //靜態(tài)代理12 // ITask dealTask=new DealTask();//獲取被代理類的對象13 // ITask proxyTask=new ProxyTask(dealTask);//獲取靜態(tài)代理類對象14 // proxyTask.doTask("打噴嚏..");15 16 // 動態(tài)代理17 ITask dealTask = new DealTask();// 獲取被代理類的對象18 JDKProxy jdkProxy = new JDKProxy(dealTask);// 獲取動態(tài)代理類對象19 /**20 * loader 類加載器 interfaces 實現(xiàn)接口 h InvocationHandler21 */22 ITask iTask = (ITask) Proxy.newProxyInstance(dealTask.getClass()23 .getClassLoader(), dealTask.getClass().getInterfaces(),24 jdkProxy);25 iTask.doTask("在發(fā)呆...");26 }27 28 }

1 package com.lcw.proxy.test; 2 3 import java.util.Random; 4 5 public class Task { 6 7 public void doTask(){ 8 System.out.println("我在完成任務(wù)"); 9 try {10 Thread.sleep(new Random().nextInt(1000));11 } catch (InterruptedException e) {12 e.printStackTrace();13 }14 }15 16 } 1 package com.lcw.proxy.test; 2 3 import java.lang.reflect.Method; 4 5 import net.sf.cglib.proxy.Enhancer; 6 import net.sf.cglib.proxy.MethodInterceptor; 7 import net.sf.cglib.proxy.MethodProxy; 8 9 /**10 * 11 * 創(chuàng)建Cglib的代理類(需要實現(xiàn)MethodInterceptor接口)12 *13 */14 public class CglibProxy implements MethodInterceptor{15 16 //需要向客戶端返回一個代理類對象17 private Enhancer enhancer=new Enhancer();18 19 public Object getProxy(Class c){20 //cglib代理的原理就是為被代理類(父類)創(chuàng)建子類21 enhancer.setSuperclass(c);22 enhancer.setCallback(this);23 return enhancer.create();24 }25 26 /**27 * 參數(shù):28 * obj被代理類的對象29 * method被代理類的方法30 * args被代理類方法的參數(shù)31 * proxy代理類對象32 */33 @Override34 public Object intercept(Object obj, Method method, Object[] args,35 MethodProxy proxy) throws Throwable {36 long startTime=System.currentTimeMillis();//記錄業(yè)務(wù)起始時間37 proxy.invokeSuper(obj, args);//cglib代理的原理就是為被代理類(父類)創(chuàng)建子類,所以這邊調(diào)用了父類super,參數(shù)一:父類的對象、參數(shù)二:方法參數(shù)38 long endTime=System.currentTimeMillis();//記錄業(yè)務(wù)結(jié)束時間39 System.out.println("本次任務(wù)執(zhí)行時間為:"+(endTime-startTime+"毫秒"));40 return null;41 }42 43 } 1 package com.lcw.proxy.test; 2 3 import java.lang.reflect.Proxy; 4 5 public class ProxyTest { 6 7 /** 8 * 測試類 9 */10 public static void main(String[] args) {11 // //靜態(tài)代理12 // ITask dealTask=new DealTask();//獲取被代理類的對象13 // ITask proxyTask=new ProxyTask(dealTask);//獲取靜態(tài)代理類對象14 // proxyTask.doTask("打噴嚏..");15 16 // 動態(tài)代理17 // ITask dealTask = new DealTask();// 獲取被代理類的對象18 // JDKProxy jdkProxy = new JDKProxy(dealTask);// 獲取動態(tài)代理類對象19 // /**20 // * loader 類加載器 interfaces 實現(xiàn)接口 h InvocationHandler21 // */22 // ITask iTask = (ITask) Proxy.newProxyInstance(dealTask.getClass()23 // .getClassLoader(), dealTask.getClass().getInterfaces(),24 // jdkProxy);25 // iTask.doTask("在發(fā)呆...");26 27 //Cglib代理28 CglibProxy cglibProxy=new CglibProxy();29 Task task=(Task) cglibProxy.getProxy(Task.class);30 task.doTask();31 32 }33 34 }

新聞熱點
疑難解答