1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        代理模式解析,靜態(tài)代理、動(dòng)態(tài)代理一文全都告訴你

        共 6472字,需瀏覽 13分鐘

         ·

        2020-11-12 22:58

        Proxy Pattern

        經(jīng)過(guò)兩個(gè)月的學(xué)習(xí),我們的設(shè)計(jì)模式系列學(xué)習(xí)也將近到了尾聲。不過(guò)設(shè)計(jì)模式這東西一時(shí)半會(huì)肯定是掌握不下來(lái)的,需要我們?cè)诤罄m(xù)的工作中持續(xù)的去練習(xí)。有了這方面的思維,剩下的就是靠自己刻意訓(xùn)練了。

        今天帶來(lái)的是代理模式,Proxy Pattern。這個(gè)模式想必大家都不陌生,因?yàn)?Java 程序員面試一般都會(huì)問(wèn)的 Spring Aop 經(jīng)過(guò)會(huì)說(shuō)到動(dòng)態(tài)代理,這其實(shí)就是代理模式的一種實(shí)現(xiàn)。不過(guò)大部分人可能都沒(méi)有系統(tǒng)的去了解過(guò),本文的目的就是徹底搞清楚代理模式的小99。

        閱讀之前也思考關(guān)于代理模式的幾個(gè)問(wèn)題,帶著搞清楚問(wèn)題的目的去學(xué)習(xí)。

        帶著問(wèn)題出發(fā)

        1. 什么是代理模式?
        2. 靜態(tài)代理?
        3. 動(dòng)態(tài)代理?
        4. 常見(jiàn)的代理模式變種?

        代理模式的介紹

        代理模式: 為一個(gè)對(duì)象提供一個(gè)替身或者占位符,以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象。這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。不過(guò)這里的側(cè)重點(diǎn)還是在于對(duì) “對(duì)象“ 訪問(wèn)的控制。

        1. 被代理的對(duì)象可以是:遠(yuǎn)程對(duì)象、創(chuàng)建開(kāi)銷(xiāo)大的對(duì)象或者需要安全控制的對(duì)象;
        2. 代理模式有不同的形式,主要有三種:靜態(tài)代理、動(dòng)態(tài)代理(JDK和Cglib);

        代理模式類(lèi)圖

        靜態(tài)代理

        靜態(tài)代理在使用時(shí),需要定義接口或者父類(lèi),被代理對(duì)象(目標(biāo)對(duì)象)與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者繼承相同的父類(lèi)

        應(yīng)用實(shí)例

        具體要求

        1. 定義一個(gè)接口:ITeacherDao
        2. 目前對(duì)象 TeacherDAO 實(shí)現(xiàn) ITeacherDao
        3. 使用靜態(tài)代理就需要在 TeacherDAOProxy 中也實(shí)現(xiàn) ITeacherDao 接口
        4. 調(diào)用的時(shí)候通過(guò)調(diào)用代理對(duì)象的方法來(lái)調(diào)用目標(biāo)對(duì)象
        5. 特別注意:代理對(duì)象要與目標(biāo)對(duì)象實(shí)現(xiàn)相同的接口,然后通過(guò)調(diào)用相同的方法來(lái)調(diào)用目標(biāo)對(duì)象的方法

        思路分析圖解(類(lèi)圖)

        代碼實(shí)現(xiàn)

        public?class?Client?{

        ?public?static?void?main(String[]?args)?{
        ??//?TODO?Auto-generated?method?stub
        ??//創(chuàng)建目標(biāo)對(duì)象(被代理對(duì)象)
        ??TeacherDao?teacherDao?=?new?TeacherDao();
        ??
        ??//創(chuàng)建代理對(duì)象,?同時(shí)將被代理對(duì)象傳遞給代理對(duì)象
        ??TeacherDaoProxy?teacherDaoProxy?=?new?TeacherDaoProxy(teacherDao);
        ??
        ??//通過(guò)代理對(duì)象,調(diào)用到被代理對(duì)象的方法
        ??//即:執(zhí)行的是代理對(duì)象的方法,代理對(duì)象再去調(diào)用目標(biāo)對(duì)象的方法?
        ??teacherDaoProxy.teach();
        ?}

        }
        //接口
        public?interface?ITeacherDao?{
        ?
        ?void?teach();?//?授課的方法
        }
        public?class?TeacherDao?implements?ITeacherDao?{

        ?@Override
        ?public?void?teach()?{
        ??//?TODO?Auto-generated?method?stub
        ??System.out.println("?老師授課中??。。。。。");
        ?}

        }
        //代理對(duì)象,靜態(tài)代理
        public?class?TeacherDaoProxy?implements?ITeacherDao{
        ?
        ?private?ITeacherDao?target;?//?目標(biāo)對(duì)象,通過(guò)接口來(lái)聚合
        ?
        ?//構(gòu)造器
        ?public?TeacherDaoProxy(ITeacherDao?target)?{
        ??this.target?=?target;
        ?}

        ?@Override
        ?public?void?teach()?{
        ??//?TODO?Auto-generated?method?stub
        ??System.out.println("開(kāi)始代理??完成某些操作。。。。。?");//方法
        ??target.teach();
        ??System.out.println("提交。。。。。");//方法
        ?}

        }

        靜態(tài)代理優(yōu)缺點(diǎn)

        1. 優(yōu)點(diǎn):再不改變目標(biāo)對(duì)象功能的前提下,能通過(guò)代理對(duì)象對(duì)目標(biāo)功能擴(kuò)展
        2. 缺點(diǎn):很明顯,因?yàn)榇韺?duì)象要和目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類(lèi)
        3. 一旦接口新增方法,目標(biāo)對(duì)象和代理對(duì)象都要維護(hù)

        動(dòng)態(tài)代理

        動(dòng)態(tài)代理這塊分為 JDK 動(dòng)態(tài)代理和 Cglib 動(dòng)態(tài)代理,我們先來(lái)看看 JDK 動(dòng)態(tài)代理是怎么玩的。

        JDK 動(dòng)態(tài)代理

        1. 代理對(duì)象,不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象要實(shí)現(xiàn)接口,否則不能用 JDK動(dòng)態(tài)代理
        2. 代理對(duì)象的生成是利用 JDK 的 API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象

        JDK 中生成代理對(duì)象的API

        1. 代理類(lèi)所在的包:java.lang.reflect.Proxy
        2. JDK 實(shí)現(xiàn)代理只需要使用 newProxyInstance 方法,但是該方法需要接收三個(gè)參數(shù),完整的寫(xiě)法是:static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

        JDK 動(dòng)態(tài)代理應(yīng)用實(shí)例

        這里我們來(lái)將上面的例子使用 JDK 動(dòng)態(tài)代理的方式 改進(jìn)成代理動(dòng)態(tài)。

        思路圖解(類(lèi)圖)

        代碼實(shí)現(xiàn)

        public?class?Client?{

        ?public?static?void?main(String[]?args)?{
        ??//?TODO?Auto-generated?method?stub
        ??//創(chuàng)建目標(biāo)對(duì)象
        ??ITeacherDao?target?=?new?TeacherDao();
        ??
        ??//給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象,?可以轉(zhuǎn)成?ITeacherDao
        ??ITeacherDao?proxyInstance?=?(ITeacherDao)new?ProxyFactory(target).getProxyInstance();
        ?
        ??//?proxyInstance=class?com.sun.proxy.$Proxy0?內(nèi)存中動(dòng)態(tài)生成了代理對(duì)象
        ??System.out.println("proxyInstance="?+?proxyInstance.getClass());
        ??
        ??//通過(guò)代理對(duì)象,調(diào)用目標(biāo)對(duì)象的方法
        ??//proxyInstance.teach();
        ??
        ??proxyInstance.sayHello("?tom?");
        ?}

        }
        //接口
        public?interface?ITeacherDao?{

        ?void?teach();?//?授課方法
        ?void?sayHello(String?name);
        }
        public?class?ProxyFactory?{

        ?//維護(hù)一個(gè)目標(biāo)對(duì)象?,?Object
        ?private?Object?target;

        ?//構(gòu)造器?,?對(duì)target?進(jìn)行初始化
        ?public?ProxyFactory(Object?target)?{
        ??
        ??this.target?=?target;
        ?}?
        ?
        ?//給目標(biāo)對(duì)象?生成一個(gè)代理對(duì)象
        ?public?Object?getProxyInstance()?{
        ??
        ??//說(shuō)明
        ??/*
        ???*??public?static?Object?newProxyInstance(ClassLoader?loader,
        ??????????????????????????????????????????Class[]?interfaces,
        ??????????????????????????????????????????InvocationHandler?h)
        ??????????????????????????????????????????
        ????????????//1. ClassLoader loader :?指定當(dāng)前目標(biāo)對(duì)象使用的類(lèi)加載器, 獲取加載器的方法固定
        ????????????//2.?Class[]?interfaces:?目標(biāo)對(duì)象實(shí)現(xiàn)的接口類(lèi)型,使用泛型方法確認(rèn)類(lèi)型
        ????????????//3.?InvocationHandler?h?:?事情處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事情處理器方法,?會(huì)把當(dāng)前執(zhí)行的目標(biāo)對(duì)象方法作為參數(shù)傳入
        ???*/

        ??return?Proxy.newProxyInstance(target.getClass().getClassLoader(),?
        ????target.getClass().getInterfaces(),?
        ????new?InvocationHandler()?{
        ?????
        ?????@Override
        ?????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
        ??????//?TODO?Auto-generated?method?stub
        ??????System.out.println("JDK代理開(kāi)始~~");
        ??????//反射機(jī)制調(diào)用目標(biāo)對(duì)象的方法
        ??????Object?returnVal?=?method.invoke(target,?args);
        ??????System.out.println("JDK代理提交");
        ??????return?returnVal;
        ?????}
        ????});?
        ?}
        ?
        }
        public?class?TeacherDao?implements?ITeacherDao?{

        ?@Override
        ?public?void?teach()?{
        ??//?TODO?Auto-generated?method?stub
        ??System.out.println("?老師授課中....?");
        ?}

        ?@Override
        ?public?void?sayHello(String?name)?{
        ??//?TODO?Auto-generated?method?stub
        ??System.out.println("hello?"?+?name);
        ?}
        ?
        }

        代碼也很簡(jiǎn)單,如果你對(duì)這里不熟悉的話,一定要寫(xiě)下上面的代碼感受下。

        Cglib動(dòng)態(tài)代理

        1. 靜態(tài)代理和 JDK 動(dòng)態(tài)代理都要求目標(biāo)對(duì)象實(shí)現(xiàn)一個(gè)接口,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)對(duì)象,并沒(méi)有實(shí)現(xiàn)任何接口,這種情況就可以使用目標(biāo)對(duì)象的子類(lèi)來(lái)實(shí)現(xiàn)代理,這就是 Cglib代理。

        2. Cglib 代理也叫子類(lèi)代理,它是通過(guò)在內(nèi)存中構(gòu)建一個(gè)子類(lèi)對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的擴(kuò)展。

        3. Cglib 是一個(gè)強(qiáng)大的高性能的代碼生成包,可以在運(yùn)行期擴(kuò)展 Java 類(lèi)與實(shí)現(xiàn) Java 接口,它廣泛的被許多 AOP 框架使用,例如 Spring AOP,實(shí)現(xiàn)方法攔截。

        4. 在 AOP 編程中如何選擇使用哪種動(dòng)態(tài)代理呢?

          1. 目標(biāo)對(duì)象實(shí)現(xiàn)接口,利用JDK動(dòng)態(tài)代理
          2. 目標(biāo)對(duì)象不需要實(shí)現(xiàn)接口,用Cglib實(shí)現(xiàn)代理
        5. Cglib 包底層是通過(guò)字節(jié)碼處理框架 ASM 來(lái)轉(zhuǎn)換字節(jié)并生成新的類(lèi)

        Cglib 動(dòng)態(tài)代理實(shí)現(xiàn)步驟

        1. 需要引入 cglib 的 jar 包

        2. 在內(nèi)存中動(dòng)態(tài)創(chuàng)建子類(lèi),因此需要代理的類(lèi)(目標(biāo)對(duì)象)不能為final,否則報(bào)錯(cuò):

        3. 如果被代理的對(duì)象的方法是 final/static 的,那么目標(biāo)方法將不會(huì)被攔截,不會(huì)執(zhí)行目標(biāo)對(duì)象方法額外的業(yè)務(wù)處理,因?yàn)?final 和 static 修飾的方法 是無(wú)法被子類(lèi)擴(kuò)展的

        應(yīng)用示例

        要求:將前面的例子用 Cglib 代理模式實(shí)現(xiàn)

        思路圖解(類(lèi)圖)

        代碼實(shí)現(xiàn)

        public?class?Client?{

        ?public?static?void?main(String[]?args)?{
        ??//?TODO?Auto-generated?method?stub
        ??//創(chuàng)建目標(biāo)對(duì)象
        ??TeacherDao?target?=?new?TeacherDao();
        ??//獲取到代理對(duì)象,并且將目標(biāo)對(duì)象傳遞給代理對(duì)象
        ??TeacherDao?proxyInstance?=?(TeacherDao)new?ProxyFactory(target).getProxyInstance();

        ??//執(zhí)行代理對(duì)象的方法,觸發(fā)intecept?方法,從而實(shí)現(xiàn)?對(duì)目標(biāo)對(duì)象的調(diào)用
        ??String?res?=?proxyInstance.teach();
        ??System.out.println("res="?+?res);
        ?}

        }
        public?class?ProxyFactory?implements?MethodInterceptor?{

        ?//維護(hù)一個(gè)目標(biāo)對(duì)象
        ?private?Object?target;
        ?
        ?//構(gòu)造器,傳入一個(gè)被代理的對(duì)象
        ?public?ProxyFactory(Object?target)?{
        ??this.target?=?target;
        ?}

        ?//返回一個(gè)代理對(duì)象:??是?target?對(duì)象的代理對(duì)象
        ?public?Object?getProxyInstance()?{
        ??//1.?創(chuàng)建一個(gè)工具類(lèi)
        ??Enhancer?enhancer?=?new?Enhancer();
        ??//2.?設(shè)置父類(lèi)
        ??enhancer.setSuperclass(target.getClass());
        ??//3.?設(shè)置回調(diào)函數(shù)
        ??enhancer.setCallback(this);
        ??//4.?創(chuàng)建子類(lèi)對(duì)象,即代理對(duì)象
        ??return?enhancer.create();
        ??
        ?}

        ?//重寫(xiě)??intercept?方法,會(huì)調(diào)用目標(biāo)對(duì)象的方法
        ?@Override
        ?public?Object?intercept(Object?arg0,?Method?method,?Object[]?args,?MethodProxy?arg3)?throws?Throwable?{
        ??//?TODO?Auto-generated?method?stub
        ??System.out.println("Cglib代理模式?~~?開(kāi)始");
        ??Object?returnVal?=?method.invoke(target,?args);
        ??System.out.println("Cglib代理模式?~~?提交");
        ??return?returnVal;
        ?}

        }
        public?class?TeacherDao?{

        ?public?String?teach()?{
        ??System.out.println("?老師授課中??,?我是cglib代理,不需要實(shí)現(xiàn)接口?");
        ??return?"hello";
        ?}
        }

        幾種常見(jiàn)代理模式的變體

        1. 防火墻代理

          1. 內(nèi)網(wǎng)通過(guò)代理實(shí)現(xiàn)對(duì)防火墻的穿透,實(shí)現(xiàn)對(duì)公網(wǎng)的訪問(wèn)
        2. 緩存代理

          1. 當(dāng)請(qǐng)求圖片等文件時(shí),先到緩存代理取,如果可以取到資源就皆大歡喜,如果取不到,再到公網(wǎng)或者數(shù)據(jù)庫(kù)取,然后緩存
        3. 遠(yuǎn)程代理

          1. 遠(yuǎn)程對(duì)象的本地代理,通過(guò)它可以把遠(yuǎn)程對(duì)象當(dāng)本地對(duì)象調(diào)用。遠(yuǎn)程代理對(duì)象通過(guò)網(wǎng)絡(luò)協(xié)議和真正的遠(yuǎn)程對(duì)象通信

        總結(jié)

        本文通過(guò)類(lèi)圖和代碼示例,對(duì)代理模式做了詳細(xì)的講解,旨在讓你掌握,代理模式的定義以及面試常問(wèn)的靜態(tài)代理、代理代理、以及 JDK 動(dòng)態(tài)代理和 Cglib 動(dòng)態(tài)代理的區(qū)別以及實(shí)現(xiàn)方式。

        全文代碼,均為筆者本地跑通過(guò)的,可以直接用來(lái)測(cè)試使用。


        全文完,fighting!

        END/往期推薦:




        1.微服務(wù)實(shí)戰(zhàn)系列

        2.springboot從入門(mén)到精通

        3.java入門(mén)到精通

        4.中間件等

        5.程序人生

        更多信息請(qǐng)關(guān)注公眾號(hào):「軟件老王」,關(guān)注不迷路,軟件老王和他的IT朋友們,分享一些他們的技術(shù)見(jiàn)解和生活故事。

        瀏覽 18
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日韩AV免费在线 | 北岛玲日韩精品一区二区三区 | 国产成人精品久 | 草美女视频网站 | 中文乱片A片AAA毛片 | 亚洲无码手机在线播放 | 妈妈你真棒插曲快来救救我电影评书 | 亚洲精品国产AV婷婷 | 国产无遮挡免费视频 | 操美女的逼真舒服 |