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>

        秒懂 Java 的三種代理模式

        共 13584字,需瀏覽 28分鐘

         ·

        2021-08-01 18:54

        作者:初念初戀

        來(lái)源:SegmentFault 思否社區(qū)

        前言

        代理(Proxy)模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪問(wèn)方式;即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象。


        這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。

        這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需要修改,可以通過(guò)代理的方式來(lái)擴(kuò)展該方法。

        代理模式大致有三種角色:

        • Real Subject:真實(shí)類,也就是被代理類、委托類。用來(lái)真正完成業(yè)務(wù)服務(wù)功能;

        • Proxy:代理類,將自身的請(qǐng)求用 Real Subject 對(duì)應(yīng)的功能來(lái)實(shí)現(xiàn),代理類對(duì)象并不真正的去實(shí)現(xiàn)其業(yè)務(wù)功能;

        • Subject:定義 RealSubject 和 Proxy 角色都應(yīng)該實(shí)現(xiàn)的接口。


        代理模式有三種類型,靜態(tài)代理,動(dòng)態(tài)代理(JDK代理,接口代理)、Cglib代理(在內(nèi)存中動(dòng)態(tài)的創(chuàng)建目標(biāo)對(duì)象的子類)

        正文

        靜態(tài)代理


        靜態(tài)代理需要先定義接口,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口,然后通過(guò)調(diào)用相同的方法來(lái)調(diào)用目標(biāo)對(duì)象的方法。

        可以看見(jiàn),代理類無(wú)非是在調(diào)用委托類方法的前后增加了一些操作。委托類的不同,也就導(dǎo)致代理類的不同。
        某公司生產(chǎn)電視機(jī),在當(dāng)?shù)劁N售需要找到一個(gè)代理銷售商。那么客戶需要購(gòu)買電視機(jī)的時(shí)候,就直接通過(guò)代理商購(gòu)買就可以。
        代碼示例:
        電視機(jī):
        public class TV {

            private String name;//名稱

            private String address;//生產(chǎn)地

            public TV(String name, String address) {
                this.name = name;
                this.address = address;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public String getAddress() {
                return address;
            }

            public void setAddress(String address) {
                this.address = address;
            }

            @Override
            public String toString() {
                return "TV{" +
                        "name='" + name + '\'' +
                        ", address='
        " + address + '\'' +
                        '}';
            }
        }
        創(chuàng)建公司接口:
        public interface TVCompany {

            /**
             * 生產(chǎn)電視機(jī)
             * @return 電視機(jī)
             */
            public TV produceTV();
        }
        公司的工廠生產(chǎn)電視機(jī):
        public class TVFactory implements TVCompany {
            @Override
            public TV produceTV() {
                System.out.println("TV factory produce TV...");
                return new TV("小米電視機(jī)","合肥");
            }
        }
        代理商去下單拿貨(靜態(tài)代理類):
        public class TVProxy implements TVCompany{

            private TVCompany tvCompany;

            public TVProxy(){

            }

            @Override
            public TV produceTV() {
                System.out.println("TV proxy get order .... ");
                System.out.println("TV proxy start produce .... ");
                if(Objects.isNull(tvCompany)){
                    System.out.println("machine proxy find factory .... ");
                    tvCompany = new TVFactory();
                }
                return tvCompany.produceTV();
            }
        }
        消費(fèi)者通過(guò)代理商拿貨(代理類的使用):
        public class TVConsumer {

            public static void main(String[] args) {
                TVProxy tvProxy = new TVProxy();
                TV tv = tvProxy.produceTV();
                System.out.println(tv);
            }
        }
        輸出結(jié)果:
        TV proxy get order .... 
        TV proxy start produce .... 
        machine proxy find factory .... 
        TV factory produce TV...
        TV{name='小米電視機(jī)', address='合肥'}

        Process finished with exit code 0

        小結(jié):

        • 優(yōu)點(diǎn):靜態(tài)代理模式在不改變目標(biāo)對(duì)象的前提下,實(shí)現(xiàn)了對(duì)目標(biāo)對(duì)象的功能擴(kuò)展。

        • 缺點(diǎn):靜態(tài)代理實(shí)現(xiàn)了目標(biāo)對(duì)象的所有方法,一旦目標(biāo)接口增加方法,代理對(duì)象和目標(biāo)對(duì)象都要進(jìn)行相應(yīng)的修改,增加維護(hù)成本。

        如何解決靜態(tài)代理中的缺點(diǎn)呢?答案是可以使用動(dòng)態(tài)代理方式

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


        動(dòng)態(tài)代理具有如下特點(diǎn):
        • JDK動(dòng)態(tài)代理對(duì)象不需要實(shí)現(xiàn)接口,只有目標(biāo)對(duì)象需要實(shí)現(xiàn)接口。

        • 實(shí)現(xiàn)基于接口的動(dòng)態(tài)代理需要利用JDK中的API,在JVM內(nèi)存中動(dòng)態(tài)的構(gòu)建Proxy對(duì)象

        • 需要使用到 java.lang.reflect.Proxy,和其newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù)。


        注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:
        • ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的。

        • Class<?>[] interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型。

        • InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入。

        有一天公司增加了業(yè)務(wù),出售的商品越來(lái)越多,售后也需要更上。但是公司發(fā)現(xiàn)原來(lái)的代理商,還要再培訓(xùn)才能完成全部的業(yè)務(wù),于是就找了另外的動(dòng)態(tài)代理商B 。 代理商B 承諾無(wú)縫對(duì)接公司所有的業(yè)務(wù),不管新增什么業(yè)務(wù),均不需要額外的培訓(xùn)即可完成。
        代碼示例:
        公司增加了維修業(yè)務(wù):
        public interface TVCompany {

            /**
             * 生產(chǎn)電視機(jī)
             * @return 電視機(jī)
             */
            public TV produceTV();

            /**
             * 維修電視機(jī)
             * @param tv 電視機(jī)
             * @return 電視機(jī)
             */
            public TV repair(TV tv);
        }
        工廠也得把維修業(yè)務(wù)搞起來(lái):
        public class TVFactory implements TVCompany {
            @Override
            public TV produceTV() {
                System.out.println("TV factory produce TV...");
                return new TV("小米電視機(jī)","合肥");
            }

            @Override
            public TV repair(TV tv) {
                System.out.println("tv is repair finished...");
                return new TV("小米電視機(jī)","合肥");
            }
        }
        B代理商 全面代理公司所有的業(yè)務(wù)。使用Proxy.newProxyInstance方法生成代理對(duì)象,實(shí)現(xiàn)InvocationHandler中的 invoke方法,在invoke方法中通過(guò)反射調(diào)用代理類的方法,并提供增強(qiáng)方法。
        public class TVProxyFactory {

            private Object target;

            public TVProxyFactory(Object o){
                this.target = o;
            }

            public Object getProxy(){
                return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
                        new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("TV proxy find factory for tv.... ");
                        Object invoke = method.invoke(target, args);
                        return invoke;
                    }
                });
            }
        }
        購(gòu)買、維修這兩個(gè)業(yè)務(wù) B代理就可以直接搞定了。后面公司再增加業(yè)務(wù),B代理也可以一樣搞定。
        public class TVConsumer {

            public static void main(String[] args) {
                TVCompany target = new TVFactory();
                TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();
                TV tv = tvCompany.produceTV();
                tvCompany.repair(tv);
            }
        }
        輸出結(jié)果:
        TV proxy find factory for tv.... 
        TV factory produce TV...
        TV proxy find factory for tv.... 
        tv is repair finished...

        Process finished with exit code 0

        小結(jié):

        • 代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理。

        • 動(dòng)態(tài)代理的方式中,所有的函數(shù)調(diào)用最終都會(huì)經(jīng)過(guò) invoke 函數(shù)的轉(zhuǎn)發(fā),因此我們就可以在這里做一些自己想做的操作,比如日志系統(tǒng)、事務(wù)、攔截器、權(quán)限控制等。

        JDK 動(dòng)態(tài)代理有一個(gè)最致命的問(wèn)題是它只能代理實(shí)現(xiàn)了某個(gè)接口的實(shí)現(xiàn)類,并且代理類也只能代理接口中實(shí)現(xiàn)的方法,要是實(shí)現(xiàn)類中有自己私有的方法,而接口中沒(méi)有的話,該方法不能進(jìn)行代理調(diào)用。
        怎么解決這個(gè)問(wèn)題呢?我們可以用 CGLIB 動(dòng)態(tài)代理機(jī)制。

        Cglib代理

        靜態(tài)代理和JDK代理都需要某個(gè)對(duì)象實(shí)現(xiàn)一個(gè)接口,有時(shí)候代理對(duì)象只是一個(gè)單獨(dú)對(duì)象,此時(shí)可以使用Cglib代理。

        Cglib代理可以稱為子類代理,是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象,從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展。
        C代理商不僅想代理公司,而且還想代理多個(gè)工廠的產(chǎn)品。
        Cglib通過(guò)Enhancer 來(lái)生成代理類,通過(guò)實(shí)現(xiàn)MethodInterceptor接口,并實(shí)現(xiàn)其中的intercept方法,在此方法中可以添加增強(qiáng)方法,并可以利用反射Method或者MethodProxy繼承類 來(lái)調(diào)用原方法。
        看到 B代理商承接了公司(接口)的多種業(yè)務(wù),那么此時(shí)C代理商又從中發(fā)現(xiàn)新的商機(jī), B 只能代理某個(gè)公司的產(chǎn)品,而我不僅想要代理公司產(chǎn)品,而且對(duì)接不同的工廠,拿貨渠道更廣,賺錢更爽快。于是Cglib就用上了。
        代碼示例:
        public class TVProxyCglib implements MethodInterceptor {

            //給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象
            public Object getProxyInstance(Class c){
                //1.工具類
                Enhancer enhancer = new Enhancer();
                //2.設(shè)置父類
                enhancer.setSuperclass(c);
                //3.設(shè)置回調(diào)函數(shù)
                enhancer.setCallback(this);
                //4.創(chuàng)建子類(代理對(duì)象)
                return enhancer.create();
            }

            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("TVProxyFactory enhancement.....");
                Object object = methodProxy.invokeSuper(o, objects);
                return object;
            }
        }
        新代理的B工廠
        public class TVFactoryB {

            public TV produceTVB() {
                System.out.println("tv factory B producing tv.... ");
                return new TV("華為電視機(jī)""南京");
            }

            public TV repairB(TV tv) {
                System.out.println("tv B is repair finished.... ");
                return tv;
            }
        }
        C代理可以直接和公司合作,也可以和工廠打交道。并且可以代理任何工廠的產(chǎn)品。
        public class TVConsumer {

            public static void main(String[] args) {
                TVCompany tvCompany = (TVCompany) new TVProxyCglib().getProxyInstance(TVFactory.class);
                TV tv = tvCompany.produceTV();
                tvCompany.repair(tv);
                System.out.println("==============================");

                TVFactoryB tvFactoryB = (TVFactoryB) new TVProxyCglib().getProxyInstance(TVFactoryB.class);
                TV tv = tvFactoryB.produceTVB();
                tvFactoryB.repairB(tv);
            }
        }
        輸出結(jié)果:
        TVProxyFactory enhancement.....
        TV factory produce TV...
        TVProxyFactory enhancement.....
        tv is repair finished...
        ==============================
        TVProxyFactory enhancement.....
        tv factory B producing tv.... 
        TVProxyFactory enhancement.....
        tv B is repair finished.... 

        Process finished with exit code 0

        Spring中AOP使用代理

        Spring中AOP的實(shí)現(xiàn)有JDK和Cglib兩種,如下圖:

        如果目標(biāo)對(duì)象需要實(shí)現(xiàn)接口,則使用JDK代理。
        如果目標(biāo)對(duì)象不需要實(shí)現(xiàn)接口,則使用Cglib代理。

        總結(jié)

        1. 靜態(tài)代理:需要代理類和目標(biāo)類都實(shí)現(xiàn)接口的方法,從而達(dá)到代理增強(qiáng)其功能。
        2. JDK動(dòng)態(tài)代理:需要代理類實(shí)現(xiàn)某個(gè)接口,使用Proxy.newProxyInstance方法生成代理類,并實(shí)現(xiàn)InvocationHandler中的invoke方法,實(shí)現(xiàn)增強(qiáng)功能。
        3. Cglib動(dòng)態(tài)代理:無(wú)需代理類實(shí)現(xiàn)接口,使用Cblib中的Enhancer來(lái)生成代理對(duì)象子類,并實(shí)現(xiàn)MethodInterceptor中的intercept方法,在此方法中可以實(shí)現(xiàn)增強(qiáng)功能。


        點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開(kāi)更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

        - END -


        瀏覽 44
        點(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>
            色中色综合网 | 国产videosfree性另类 | 美女操屄视频 | 久热在线精品 | 极品漂亮人妻找猛男3p | 寝室里的高潮h百合 | 岛国av噜噜噜久久久狠狠AV | 天天干天天在线观看 | 军部女兵AAA毛片 | 人妻首拍|