android常用設(shè)計(jì)模式之代理設(shè)計(jì)模式及動(dòng)態(tài)代理原理

定義:代理模式屬結(jié)構(gòu)型設(shè)計(jì)模式。為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。

代理模式結(jié)構(gòu)圖

代理類結(jié)構(gòu)圖.jpg

在代理模式中有如下角色:

  • ISubject: 抽象主題類,聲明真實(shí)主題與代理的共同接口方法。
  • RealSubject:真實(shí)主題類,代理類所代表的真實(shí)主題??蛻舳送ㄟ^代理類間接地調(diào)用真實(shí)主題類的方法。
  • Proxy:代理類,持有對(duì)真實(shí)主題類的引用,在其所實(shí)現(xiàn)的接口方法中調(diào)用真實(shí)主題類中相應(yīng)的接口方法執(zhí)行。

1. 簡(jiǎn)單實(shí)現(xiàn)代碼

public interface IShop {
    void buy();
}

public class BuyProxy implements IShop {

    private IShop mShop;

    public BuyProxy(IShop shop){
        mShop = shop;
    }

    @Override
    public void buy() {
        mShop.buy();
    }
}

public class Customer implements IShop {
    @Override
    public void buy() {
        System.out.print("顧客購(gòu)物");
    }
}

public class BuyProxyTest {
    private Customer mCustomer;
    private BuyProxy mBuyProxy;
    @Before
    public void setUp() throws Exception {
        mCustomer = new Customer();
        mBuyProxy = new BuyProxy(mCustomer);
    }

    @Test
    public void buy() throws Exception {
        mBuyProxy.buy();
    }

}

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

從編碼角度來(lái)說(shuō),代理模式分為靜態(tài)代理和動(dòng)態(tài)代理。上面的例子是靜態(tài)代理,在代碼運(yùn)行前就已經(jīng)存在了代理類的class編譯文件;而動(dòng)態(tài)代理則是在代碼運(yùn)行時(shí)通過反射來(lái)動(dòng)態(tài)生成類并確定代理誰(shuí)。Java提供動(dòng)態(tài)代理接口InvocationHandler,實(shí)現(xiàn)該接口需要重寫invoke方法。
下面用動(dòng)態(tài)代理實(shí)現(xiàn)上面的例子,代碼如下:

//動(dòng)態(tài)代理類
public class DynamicBuyProxy implements InvocationHandler {
    IShop shop;

    public DynamicBuyProxy setShop(IShop shop) {
        this.shop = shop;
        return this;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object resutl = method.invoke(shop, args);
        if("buy".equals(method.getName())){
            System.out.println("---通過動(dòng)態(tài)代理購(gòu)買---");
        }
        return null;
    }
}

//動(dòng)態(tài)代理模式單元測(cè)試類
public class DynamicBuyProxyTest {
    DynamicBuyProxy mDynamicProxy;
    IShop mCustomer;
    ClassLoader mClassLoader;
    @Before
    public void setUp() throws Exception {
        mCustomer = new Customer();
        mDynamicProxy = new DynamicBuyProxy();
        mDynamicProxy.setShop(mCustomer);
        mClassLoader = mCustomer.getClass().getClassLoader();
    }

    @Test
    public void invoke() throws Exception {
        IShop proxyer = (IShop) Proxy.newProxyInstance(mClassLoader, new Class[]{IShop.class}, mDynamicProxy);
        proxyer.buy();
        System.out.println("proxyer.classname:"+proxyer.getClass().getName()); //Attend01
    }
}

注意上面代碼注釋Attend01處, 我們輸出了動(dòng)態(tài)代理為我們生成的代理類對(duì)象類型。
執(zhí)行單元測(cè)試后結(jié)果如下:


dynamicproxy.png

意料之中的是代理類正常的輸出了我們想要的代理類邏輯。
而代理類類型卻出乎我們意料com.sun.proxy.$Proxy5,從這里引出它的原理。

原理

實(shí)際上通過 Proxy.newProxyInstance 創(chuàng)建的代理對(duì)象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對(duì)象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對(duì)象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最后一個(gè)數(shù)字表示對(duì)象的標(biāo)號(hào)。
下面來(lái)看它的源碼:

import java.lang.reflect.InvocationHandler;   
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;   
import java.lang.reflect.UndeclaredThrowableException;  
   
public final class $Proxy0 extends Proxy implements IShop {  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  
  
    static {  
        try {  
            m1 = Class.forName("java.lang.Object").getMethod("equals",  
                    new Class[] { Class.forName("java.lang.Object") });  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                    new Class[0]);  
            m3 = Class.forName("com.zyl.designpatterns.structuralpatterns.proxy.IShop").getMethod("buy",  
                    new Class[0]);  
            m2 = Class.forName("java.lang.Object").getMethod("toString",  
                    new Class[0]);  
        } catch (NoSuchMethodException nosuchmethodexception) {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        } catch (ClassNotFoundException classnotfoundexception) {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
  
    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  
  
    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                    .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public void buy() {  
        try {  
            super.h.invoke(this, m3, null);  //Attend02
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
  
    }  
}  

可以看到上面代碼注釋Attend02中h實(shí)際上就是我們的InvocationHandler接口的實(shí)現(xiàn)類DynamicBuyProxy,調(diào)用它的invoke方法就是調(diào)用了我們InvocationHandler.invoke()方法。
是不是豁然開朗了,實(shí)際上它就是JVM為我們生成了一個(gè)代理類,靜態(tài)代理是我們編譯之前寫好的, 而動(dòng)態(tài)代理是由JVM根據(jù)我們提供的接口為我們動(dòng)態(tài)生成的。

場(chǎng)景

是不是感覺用它的地方不多呢,但是實(shí)際上動(dòng)態(tài)代理場(chǎng)景有很多,比如Spring的核心AOP、Android最近大火的Retrofit等等。

優(yōu)點(diǎn)

  • 真實(shí)主題類就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯,不用關(guān)心其他的非本職工作。
  • 任何主題類隨時(shí)都會(huì)該發(fā)生變化,但是因?yàn)樗鼘?shí)現(xiàn)了公共接口,所以代理類可以不做任何修改就能夠使用。

如果對(duì)動(dòng)態(tài)代理的作用還是比較模糊, 建議看看這篇知乎的解答Java 動(dòng)態(tài)代理作用是什么?

代碼已上傳github

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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