名字看着有點繞, 但目的其實很簡單明確: 就是想實現動態代理的對象實例, 在運行時也可以切換.
先理解前提條件和程序上下文, 譬若有以下接口:java
public interface Responder { void onMethod1(String s); int onMethod2(); void onMethod3(); }
咱們將接口的一個實例Responder r1
傳入了一個別的類p1 = new Presenter(r1)
(或者外部SDK), 在運行時咱們生成了不一樣的Responder實例r2, 如今但願r2可以替換r1, 但對於Presenter類是沒法感知, 不用關心的. 顯然咱們的程序上下文可以實現對於Responder實例的控制(建立/傳遞), 但如今問題是Presenter類僅有構造參數對Responder的傳入, 沒有setResponder(Responder r)
這樣的方法(若是存在setResponder
這樣的方法, 但就沒這坨事了:). 能不能再建立一個Presenter實例p2再傳入r2呢? 若是程序上下文容許的話也沒這坨事了.app
因此條件是這樣: 接口的不一樣實例須要傳入一個對象, 但這個對象持有的實例卻沒法更改, 同時這個對象也沒法再次建立.ide
說這麼多不就是要用代理模式嗎? 不錯, 代理模式正是能夠解決這類問題的. 表述這麼累贅是想關注問題的場景, 而不是爲了生搬硬套模式.代理
因而一個簡單的代理類出來了:code
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(); } }
由於還要動態的改變代理對象因此添加一個set方法:對象
void setResponder(Responder r) { impl = r; }
那麼傳入Presenter對象的實例就再也不是r1了, 而是接口
wrapper = new ResponderWrapper(r1); p1 = new Presenter(wrapper);
這時建立了新的Responder實例r2, 咱們只須要get
wrapper.setResponder(r2);
就可以達到咱們的目的了! p1仍是p1, p1持有的實例仍是同一個實例, 在切換前p1調的是r1的實現, 切換後天然就調用了r2的實現.
這種代理就是很是常見的靜態代理, 僅就功能實現來講這已經徹底OK了, 沒有任何問題了. 是否是非得用動態代理? 並非!io
那動態代理是幹嘛的? 爲了適應變化, 什麼的變化? 接口的變化! 若是接口Responder新增一個方法, ResponderWrapper再增長一樣一個接口; 若是修改Responder一個方法的參數, ResponderWrapper再接着修改並調用接口實例的新方法, 如此類推, 也沒任何問題. 但接口的方法一旦變的不少, 接口的實現類一旦變的不少, 就須要作大量繁瑣重複的工做, 那麼動態代理就可以解決這種重複繁瑣的工做.
以動態代理的形式寫一個ResponderWrapper很是簡單:class
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); } }); } }
可是這樣寫沒法知足動態切換的需求, 因此咱們的最終目的這纔出來了: 以動態代理形式建立的代理實例可以動態切換持有的對象實例
但一旦ResponderWrapper.wrap
傳入r1那麼匿名對象持有的Responder對象就只能一直是r1, 因此但願method.invoke(responder, args)
這裏的responder
可以動態切換, 這種"動態"能力通常都是以接口的形式實現, 因而有:
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); } }); }
程序上下文實現ResponderWrapper.Provider
接口, 當接口方法被調用時返回的實例是當前的Responder, 不用關心何時切換:
mResonder = r1; wrapper = ResponderWrapper.wrap(new ResponderWrapper.Provider() { @Override public ResponderWrapper.Responder get() { return mResponder; } }); p1 = new Presenter(wrapper); ... mResonder = r2;
若是以爲接口過重, 其實這種形式也徹底能夠不用接口的方式實現, 由於咱們最終須要的實際上是一個Responder實例, 在接口方法被調用的時候可以調用這個實例的對應的方法而已, 因此能夠寫成這樣:
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<T> { public T responder; } @SuppressWarnings("unchecked") public static <T> T wrap(final Holder<T> 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
來獲取ClassLoade
r和Class<?>[]
, 也徹底能夠將Class對象傳入:
public final class ResponderWrapper { public static final class Holder<T> { public T responder; } @SuppressWarnings("unchecked") public static <T> T wrap(final Holder<T> holder, final Class<T> 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); } }); } }
這就是咱們所謂的動態切換的動態代理了.