摘要:名字看著有點繞但目的其實很簡單明確就是想實現(xiàn)動態(tài)代理的對象實例在運行時也能夠切換先理解前提條件和程序上下文譬如有如下接口我們將接口的一個實例傳入了一個別的類或者外部在運行時我們生成了不同的實例現(xiàn)在希望能夠替換但對于類是無法感知不用關心的顯然
名字看著有點繞, 但目的其實很簡單明確: 就是想實現(xiàn)動態(tài)代理的對象實例, 在運行時也能夠切換.
先理解前提條件和程序上下文, 譬如有如下接口:
public interface Responder { void onMethod1(String s); int onMethod2(); void onMethod3(); }
我們將接口的一個實例Responder r1傳入了一個別的類p1 = new Presenter(r1)(或者外部SDK), 在運行時我們生成了不同的Responder實例r2, 現(xiàn)在希望r2能夠替換r1, 但對于Presenter類是無法感知, 不用關心的. 顯然我們的程序上下文能夠實現(xiàn)對于Responder實例的控制(創(chuàng)建/傳遞), 但現(xiàn)在問題是Presenter類僅有構造參數(shù)對Responder的傳入, 沒有setResponder(Responder r)這樣的方法(如果存在setResponder這樣的方法, 但就沒這坨事了:). 能不能再創(chuàng)建一個Presenter實例p2再傳入r2呢? 如果程序上下文允許的話也沒這坨事了.
所以條件是這樣: 接口的不同實例需要傳入一個對象, 但這個對象持有的實例卻無法更改, 同時這個對象也無法再次創(chuàng)建.
說這么多不就是要用代理模式嗎? 不錯, 代理模式正是可以解決這類問題的. 表述這么累贅是想關注問題的場景, 而不是為了生搬硬套模式.
于是一個簡單的代理類出來了:
public class ResponderWrapper implements Responder { private final Responder impl; public ResponderWrapper(Responder r) { impl = r; } @Override void onMethod1(String s) { impl.onMethod1(s); } @Override int onMethod2() { return impl.onMethod2(); } @Override void onMethod3() { impl.onMethod3(); } }
因為還要動態(tài)的改變代理對象所以添加一個set方法:
void setResponder(Responder r) { impl = r; }
那么傳入Presenter對象的實例就不再是r1了, 而是
wrapper = new ResponderWrapper(r1); p1 = new Presenter(wrapper);
這時創(chuàng)建了新的Responder實例r2, 我們只需要
wrapper.setResponder(r2);
就能夠達到我們的目的了! p1還是p1, p1持有的實例還是同一個實例, 在切換前p1調(diào)的是r1的實現(xiàn), 切換后自然就調(diào)用了r2的實現(xiàn).
這種代理就是非常常見的靜態(tài)代理, 僅就功能實現(xiàn)來說這已經(jīng)完全OK了, 沒有任何問題了. 是不是非得用動態(tài)代理? 并不是!
那動態(tài)代理是干嗎的? 為了適應變化, 什么的變化? 接口的變化! 如果接口Responder新增一個方法, ResponderWrapper再增加同樣一個接口; 如果修改Responder一個方法的參數(shù), ResponderWrapper再接著修改并調(diào)用接口實例的新方法, 如此類推, 也沒任何問題. 但接口的方法一旦變的很多, 接口的實現(xiàn)類一旦變的很多, 就需要做大量繁瑣重復的工作, 那么動態(tài)代理就能夠解決這種重復繁瑣的工作.
以動態(tài)代理的形式寫一個ResponderWrapper非常簡單:
public final class ResponderWrapper { public static Responder wrap(final Responder responder) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(responder, args); } }); } }
但是這樣寫無法滿足動態(tài)切換的需求, 所以我們的最終目的這才出來了: 以動態(tài)代理形式創(chuàng)建的代理實例能夠動態(tài)切換持有的對象實例
但一旦ResponderWrapper.wrap傳入r1那么匿名對象持有的Responder對象就只能一直是r1, 所以希望method.invoke(responder, args)這里的responder能夠動態(tài)切換, 這種"動態(tài)"能力一般都是以接口的形式實現(xiàn), 于是有:
public final class ResponderWrapper { public interface Provider { Responder get(); } public static Responder wrap(final Provider provider) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(provider.get(), args); } }); }
程序上下文實現(xiàn)ResponderWrapper.Provider接口, 當接口方法被調(diào)用時返回的實例是當前的Responder, 不用關心什么時候切換:
mResonder = r1; wrapper = ResponderWrapper.wrap(new ResponderWrapper.Provider() { @Override public ResponderWrapper.Responder get() { return mResponder; } }); p1 = new Presenter(wrapper); ... mResonder = r2;
如果覺得接口太重, 其實這種形式也完全可以不用接口的方式實現(xiàn), 因為我們最終需要的其實是一個Responder實例, 在接口方法被調(diào)用的時候能夠調(diào)用這個實例的對應的方法而已, 所以可以寫成這樣:
public final class ResponderWrapper { public static final class Holder { public Responder responder; } public static Responder wrap(final Holder holder) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
程序上下文持有ResponderWrapper.Holder的實例, 再在需要的時候設置不同的Resonder實例:
mHolder = new ResponderWrapper.Holder(r1); wrapper = ResponderWrapper.wrap(holder) p1 = new Presenter(wrapper); ... mHolder.responder = r2
如果用范型抽象所有接口類, 就可以寫的更通用一點:
public final class ResponderWrapper { public static final class Holder{ public T responder; } @SuppressWarnings("unchecked") public static T wrap(final Holder holder) { T r = holder.responder; return (T) Proxy.newProxyInstance(r.getClass().getClassLoader(), r.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
這里臨時利用holder.responder來獲取ClassLoader和Class>[], 也完全可以將Class對象傳入:
public final class ResponderWrapper { public static final class Holder{ public T responder; } @SuppressWarnings("unchecked") public static T wrap(final Holder holder, final Class clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
這就是我們所謂的動態(tài)切換的動態(tài)代理了.
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/74775.html
摘要:簡單工廠模式的實質(zhì)是由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應該創(chuàng)建哪一個產(chǎn)品類。中的就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標識來獲得對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定。 設計模式作為工作學習中的枕邊書,卻時常處于勤說不用的尷尬境地,也不是我們時常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無論是在架構設計方面,還是在代碼編寫方面,都堪...
摘要:簡單工廠模式的實質(zhì)是由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應該創(chuàng)建哪一個產(chǎn)品類。中的就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標識來獲得對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定。 設計模式作為工作學習中的枕邊書,卻時常處于勤說不用的尷尬境地,也不是我們時常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無論是在架構設計方面,還是在代碼編寫方面,都堪...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區(qū)分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內(nèi)容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數(shù)據(jù)結構與算法 應用模塊: 常用工具集 ...
摘要:看下圖所示,摘自網(wǎng)絡的創(chuàng)建流程源碼分析實例是使用建造者模式通過類進行創(chuàng)建的。創(chuàng)建了一個含有對象實例的,并返回給源碼分析添加一個調(diào)用適配器工廠,用于支持服務方法返回類型注意生產(chǎn)的是,那么又是什么呢可以看到源代碼如下所示,它是一個接口。 目錄介紹 1.首先回顧Retrofit簡單使用方法 2.Retrofit的創(chuàng)建流程源碼分析 2.1 Retrofit對象調(diào)用Builder()源碼解...
閱讀 3237·2021-09-28 09:42
閱讀 3529·2021-09-22 15:21
閱讀 1211·2021-07-29 13:50
閱讀 3757·2019-08-30 15:56
閱讀 3443·2019-08-30 15:54
閱讀 1267·2019-08-30 13:12
閱讀 1260·2019-08-29 17:03
閱讀 1263·2019-08-29 10:59