動態切換的動態代理

名字看着有點繞, 但目的其實很簡單明確: 就是想實現動態代理的對象實例, 在運行時也可以切換. 先理解前提條件和程序上下文, 譬若有以下接口: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呢? 若是程序上下文容許的話也沒這坨事了.bash

因此條件是這樣: 接口的不一樣實例須要傳入一個對象, 但這個對象持有的實例卻沒法更改, 同時這個對象也沒法再次建立.app

說這麼多不就是要用代理模式嗎? 不錯, 代理模式正是能夠解決這類問題的. 表述這麼累贅是想關注問題的場景, 而不是爲了生搬硬套模式.ide

因而一個簡單的代理類出來了:spa

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了, 而是code

wrapper = new ResponderWrapper(r1);
p1 = new Presenter(wrapper);
複製代碼

這時建立了新的Responder實例r2, 咱們只須要對象

wrapper.setResponder(r2);
複製代碼

就可以達到咱們的目的了! p1仍是p1, p1持有的實例仍是同一個實例, 在切換前p1調的是r1的實現, 切換後天然就調用了r2的實現. 這種代理就是很是常見的靜態代理, 僅就功能實現來講這已經徹底OK了, 沒有任何問題了. 是否是非得用動態代理? 並非!接口

那動態代理是幹嘛的? 爲了適應變化, 什麼的變化? 接口的變化! 若是接口Responder新增一個方法, ResponderWrapper再增長一樣一個接口; 若是修改Responder一個方法的參數, ResponderWrapper再接着修改並調用接口實例的新方法, 如此類推, 也沒任何問題. 但接口的方法一旦變的不少, 接口的實現類一旦變的不少, 就須要作大量繁瑣重複的工做, 那麼動態代理就可以解決這種重複繁瑣的工做. 以動態代理的形式寫一個ResponderWrapper很是簡單:get

public final class ResponderWrapper {
    public static Responder wrap(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(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來獲取ClassLoader和Class<?>[], 也徹底能夠將Class對象傳入:

public final class ResponderWrapper {
    public static final class Holder<T> {
        public T responder;
    }

    @SuppressWarnings("unchecked")
    public static <T> T wrap(Holder<T> holder, 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);
                    }
                });
    }
}
複製代碼

這就是咱們所謂的動態切換的動態代理了.

相關文章
相關標籤/搜索