代理設(shè)計(jì)模式

1、 什么是代理設(shè)計(jì)模式

即Proxy Pattern,23種常用的面向?qū)ο筌浖脑O(shè)計(jì)模式之一。為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象或者為了某種簡(jiǎn)潔方便,而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。

生活中我們處處會(huì)見(jiàn)到代理,比如搶票軟件、各種中介(賣(mài)房子的、租房的、婚介等)還有代理服務(wù)器,這寫(xiě)其實(shí)和我們今天說(shuō)的代理設(shè)計(jì)模式原理是一樣的,中介幫我們中很多事,讓我們省心、省事,只要把我們要做的是委托給代理就ok了,中介當(dāng)然要費(fèi)用或者好處了。。一些代理或者說(shuō)中介可能會(huì)做一些我們不知道的事,比如偷工減料、增加我們不想要的或者價(jià)值很低的東西等等,同理代理設(shè)計(jì)模式也會(huì)有這種問(wèn)題。

2、 什么時(shí)候使用代理設(shè)計(jì)模式及好處

好處:①職責(zé)清晰:被代理對(duì)象只用關(guān)注自己的業(yè)務(wù)邏輯,不用關(guān)心與其他模塊或者調(diào)用者的交互,具體交互交由代理對(duì)象實(shí)現(xiàn),這樣編程比較清晰,而且符合生活場(chǎng)景

② 降低系統(tǒng)耦合以及符合開(kāi)閉原則:代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用,這樣就可以保護(hù)目標(biāo)對(duì)象。

③高擴(kuò)展性

缺點(diǎn):①可能增加復(fù)雜度②可靠性可能降低,畢竟代理對(duì)象可以在代理的時(shí)候增加一些“暗箱操作”,這就需要代理對(duì)象公開(kāi)透明其操作

使用場(chǎng)景:

遠(yuǎn)程代理,為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣就可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí)。 虛擬代理,是根據(jù)需要?jiǎng)?chuàng)建開(kāi)銷(xiāo)很大的對(duì)象。通過(guò)它來(lái)存放實(shí)例化需要很長(zhǎng)時(shí)間的真是對(duì)象。 安全代理,用來(lái)控制真實(shí)對(duì)象訪問(wèn)時(shí)的權(quán)限。 智能指引,是指當(dāng)調(diào)用真是的對(duì)象時(shí),代理處理另外的一些事情。

3、 代理設(shè)計(jì)模式的JAVA實(shí)現(xiàn)

代理設(shè)計(jì)模式結(jié)構(gòu)實(shí)現(xiàn)一般包含兩個(gè)對(duì)象一個(gè)接口①真正要訪問(wèn)的對(duì)象(目標(biāo)對(duì)象)②代理對(duì)象③代理對(duì)象和目標(biāo)對(duì)象之間共同實(shí)現(xiàn)的接口;代理對(duì)象是作為直接被client調(diào)用的對(duì)象,而其真正調(diào)用則是目標(biāo)對(duì)象,其關(guān)系如下圖:

image
   靜態(tài)代理:代理對(duì)象是由程序猿創(chuàng)建的,而且一個(gè)Proxy一般就只能代理一類(lèi)目標(biāo)對(duì)象(實(shí)現(xiàn)共同Subject接口),相當(dāng)于代理類(lèi)和被代理類(lèi)關(guān)系提前要確定。

動(dòng)態(tài)代理:在實(shí)現(xiàn)階段不用關(guān)心代理類(lèi)的具體實(shí)現(xiàn),而是在運(yùn)行時(shí)才生成代理對(duì)象(JAVA中是通過(guò)反射實(shí)現(xiàn),具體見(jiàn)下文)平臺(tái)或者三方庫(kù)有提供。動(dòng)態(tài)代理的擴(kuò)展性更高,所以一般一些開(kāi)源框架都會(huì)使用。知道概念及大概原理代碼實(shí)現(xiàn)就是輕輕松松的事了,一下是動(dòng)態(tài)代理和靜態(tài)代理JAVA實(shí)現(xiàn)的簡(jiǎn)單demo

1、靜態(tài)代理代碼實(shí)現(xiàn):

代理接口

interface Subject {
    void doAction();
}

目標(biāo)對(duì)象

class RealASubject implements Subject{
    @Override
    public void doAction() {
        System.out.println("this is realASubject");
    }
}
class RealBSubject implements Subject{
    @Override
    public void doAction() {
        System.out.println("this is realBSubject");
    }
}

代理對(duì)象

class ProxySubject implements Subject {
    public Subject subject;
    public ProxySubject (Subject subject) {//也可以通過(guò)set方法設(shè)置subject
        this.subject = subject;
    }
    @Override
    public void doAction() {
        //subject的doAction調(diào)用之前,可以做很多之前操作
        before();
        subject.doAction();
        after();//被代理對(duì)象執(zhí)行之后處理代碼
    }

    public void before(){
        System.out.println("this is execute before subject");
    }
    public void after() {
        System.out.println("this is execute after subject");
    }
}

client代碼及結(jié)果

public static void main(String[] args) {
        Subject proxySubject = new ProxySubject(new RealASubject());
        proxySubject.doAction();
    }
this is execute before subject
this is realASubject
this is execute after subject
2、動(dòng)態(tài)代理代碼實(shí)現(xiàn)及原理:

動(dòng)態(tài)代理可以通過(guò)JDK和CGLib(一個(gè)開(kāi)源工具,spring 和hibernate已經(jīng)廣泛使用)兩種方式實(shí)現(xiàn)。JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理,而不能針對(duì)類(lèi) ;CGLIB是針對(duì)類(lèi)實(shí)現(xiàn)代理,主要是對(duì)指定的類(lèi)生成一個(gè)子類(lèi),覆蓋其中的方法 ,因?yàn)槭抢^承,所以該類(lèi)或方法最好不要聲明成final 。CGLib暫時(shí)沒(méi)研究,聽(tīng)過(guò)性能比JDK實(shí)現(xiàn)要好,等有時(shí)間做一個(gè)對(duì)比研究。

動(dòng)態(tài)代理類(lèi)及其方法

class DynamicProxy implements InvocationHandler {
    public Object mTarget = null;

    public Object bind(Object obj) {
        this.mTarget = obj;
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before();
        Object obj = method.invoke(mTarget, objects);
        after();
        return obj;
    }
    public void before(){
        System.out.println("this is execute before subject for DynamicProxy");
    }
    public void after() {
        System.out.println("this is execute after subject for DynamicProxy");
    }
}

客戶(hù)端代碼及結(jié)果

public static void main(String[] args) {
        DynamicProxy dynamicProxy = new DynamicProxy();
        Subject sub = (Subject) dynamicProxy.bind(new RealBSubject());
        sub.doAction();
    }
this is execute before subject for DynamicProxy
this is realBSubject
this is execute after subject for DynamicProxy

動(dòng)態(tài)代理實(shí)現(xiàn)更具擴(kuò)展性,并且一個(gè)代理類(lèi)可以代理很多subject,使用時(shí)注意==①最后方法的調(diào)用一定要轉(zhuǎn)為接口,使用Subject.doAction()②method.invoke(mTarget,objects)的調(diào)用一定是在InvocationHandler或者子類(lèi)的invoke方法中。動(dòng)態(tài)代理實(shí)現(xiàn)是通過(guò)反射機(jī)制實(shí)現(xiàn)的,具體原理繼續(xù)下看---》》》》

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

可能愛(ài)思考的你可能會(huì)問(wèn),為什么動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理?答案:

因?yàn)樵趧?dòng)態(tài)代理過(guò)程中,會(huì)生成對(duì)應(yīng)的代理類(lèi)形如$Proxy0,$Proxy1....這樣的匿名類(lèi),這些類(lèi)都是繼承java.lang.reflect.Proxy類(lèi)和實(shí)現(xiàn)了我們傳入的接口,形如:public final class $Proxy0 extends Proxy implements Subject .所以,通過(guò)Proxy.newProxyInstance方法返回的就是這個(gè)匿名類(lèi)的實(shí)例。通常就通過(guò)強(qiáng)制轉(zhuǎn)換成指定接口,最后就可以調(diào)用方法了?!緅ava中不能多重繼承,當(dāng)代理匿名類(lèi)實(shí)現(xiàn)了jdk中的Proxy.class類(lèi)的時(shí)候,就只能通過(guò)實(shí)現(xiàn)目標(biāo)接口的方式來(lái)實(shí)現(xiàn)拓展】

Proxy.newProxyInstance()方法是動(dòng)態(tài)代理人口,newProxyInstance方法會(huì)傳入類(lèi)裝載器,InvocationHandler對(duì)象以及接口以便反射的時(shí)候使用。根據(jù)類(lèi)加載器和目標(biāo)接口類(lèi)獲取代理類(lèi)Class對(duì)象:這部分也是代理類(lèi)的核心,因?yàn)檫@方法里面包含了代理類(lèi)的動(dòng)態(tài)創(chuàng)建過(guò)程。會(huì)生成一個(gè)形如$Proxy0...之類(lèi)的動(dòng)態(tài)代理類(lèi),然后JVM會(huì)加載這些字節(jié)類(lèi),得到對(duì)應(yīng)的Class對(duì)象,進(jìn)行緩存。由于篇幅的原因newProxyInstance方法的源碼以及其調(diào)用的getProxyClass0進(jìn)行代理類(lèi)字節(jié)碼對(duì)象生成方法的源碼就不粘貼了有興趣的同學(xué)可以自己看下源碼。這里提供一個(gè)文章,對(duì)動(dòng)態(tài)代理分析的比較好動(dòng)態(tài)代理原理分析 文章中介紹:代碼中設(shè)置:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),會(huì)在項(xiàng)目根目錄生成class字節(jié)文件,通過(guò)反編譯可以看到生成的代理對(duì)象的,對(duì)象中有對(duì)應(yīng)方法和接口中定義的方法名以及參數(shù)相同如下:

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m2;

    //這就是為什么需要將InvocationHandler.class出入構(gòu)造器來(lái)查找構(gòu)造器實(shí)例的原理
    public $Proxy0(InvocationHandler paramInvocationHandler) throws {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) throws {
        try {
            return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    //這個(gè)是我們要調(diào)用的目標(biāo)類(lèi)中方法
    public final void doAction() throws {
        try {
            //實(shí)際上可以看到,是調(diào)用了我們自定義的InvocationHandler接口實(shí)現(xiàn)類(lèi)的invoke方法。
            // m3這個(gè)Method對(duì)象,是通過(guò)反射獲取
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final int hashCode() throws {
        try {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final String toString() throws {
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    //靜態(tài)代碼塊,用于通過(guò)反射來(lái)初始化四個(gè)方法屬性
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("proxy.Subject").getMethod("hello", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

看到這個(gè)親們可能就恍然大悟了,,今天就先到這里吧、、!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容