国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

軟件設(shè)計模式之代理模式(JAVA)

2019-11-14 22:28:40
字體:
供稿:網(wǎng)友
軟件設(shè)計模式之代理模式(java)

貌似停筆了近半個月了,實在不該啊,新的一年,時刻讓自己歸零。

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)它。

 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 }

接下來是被代理類,這個類實現(xiàn)了代理接口,并執(zhí)行主要的業(yè)務(wù)邏輯操作。

 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 }

再來是代理類,它同樣的實現(xiàn)了代理接口,這樣一來代理類和被代理類的對象既被"同化",通過構(gòu)造器可以對被代理類的對象進行引用,從而達到可以訪問被代理類的主要業(yè)務(wù)方法。

 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 }

看下運行效果:

到這里,靜態(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接口)

 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 }

看下實現(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ù)邏輯類

 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 }

再來一個Cglib代理類,提供代理對象,這里需要實現(xiàn)MethodInterceptor接口

 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 }

運行效果:

作者:Balla_兔子出處:http://m.survivalescaperooms.com/lichenwei/本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我!


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 凌源市| 玉田县| 泽库县| 从化市| 大名县| 扎鲁特旗| 白河县| 德保县| 东港市| 安陆市| 喀喇| 禄丰县| 德令哈市| 秦皇岛市| 阿拉善左旗| 兴和县| 杭州市| 闻喜县| 克拉玛依市| 正定县| 开江县| 郧西县| 永登县| 碌曲县| 陆川县| 澳门| 囊谦县| 镇平县| 塘沽区| 齐齐哈尔市| 河东区| 扶余县| 龙井市| 百色市| 河津市| 嘉黎县| 全南县| 惠水县| 深泽县| 阜平县| 手游|