摘要:名字看着有点绕但目的其实很简单明确就是想实现动态代理的对象实例在运行时也能够切换先理解前提条件和程序上下文譬如有如下接口我们将接口的一个实例传入了一个别的类或者外部在运行时我们生成了不同的实例现在希望能够替换但对于类是无法感知不用关心的显然
名字看着有点绕, 但目的其实很简单明确: 就是想实现动态代理的对象实例, 在运行时也能够切换.
先理解前提条件和程序上下文, 譬如有如下接口:
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呢? 如果程序上下文允许的话也没这坨事了.
所以条件是这样: 接口的不同实例需要传入一个对象, 但这个对象持有的实例却无法更改, 同时这个对象也无法再次创建.
说这么多不就是要用代理模式吗? 不错, 代理模式正是可以解决这类问题的. 表述这么累赘是想关注问题的场景, 而不是为了生搬硬套模式.
于是一个简单的代理类出来了:
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, 我们只需要
wrapper.setResponder(r2);
就能够达到我们的目的了! p1还是p1, p1持有的实例还是同一个实例, 在切换前p1调的是r1的实现, 切换后自然就调用了r2的实现.
这种代理就是非常常见的静态代理, 仅就功能实现来说这已经完全OK了, 没有任何问题了. 是不是非得用动态代理? 并不是!
那动态代理是干吗的? 为了适应变化, 什么的变化? 接口的变化! 如果接口Responder新增一个方法, ResponderWrapper再增加同样一个接口; 如果修改Responder一个方法的参数, ResponderWrapper再接着修改并调用接口实例的新方法, 如此类推, 也没任何问题. 但接口的方法一旦变的很多, 接口的实现类一旦变的很多, 就需要做大量繁琐重复的工作, 那么动态代理就能够解决这种重复繁琐的工作.
以动态代理的形式写一个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); } }); } }
但是这样写无法满足动态切换的需求, 所以我们的最终目的这才出来了: 以动态代理形式创建的代理实例能够动态切换持有的对象实例
但一旦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{ 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); } }); } }
这就是我们所谓的动态切换的动态代理了.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/74775.html
摘要:简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。中的就是简单工厂模式的体现,根据传入一个唯一的标识来获得对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。 设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬境地,也不是我们时常忘记,只是一直没有记忆。 Spring作为业界的经典框架,无论是在架构设计方面,还是在代码编写方面,都堪...
摘要:简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。中的就是简单工厂模式的体现,根据传入一个唯一的标识来获得对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。 设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬境地,也不是我们时常忘记,只是一直没有记忆。 Spring作为业界的经典框架,无论是在架构设计方面,还是在代码编写方面,都堪...
摘要:如问到是否使用某框架,实际是是问该框架的使用场景,有什么特点,和同类可框架对比一系列的问题。这两个方向的区分点在于工作方向的侧重点不同。 [TOC] 这是一份来自哔哩哔哩的Java面试Java面试 32个核心必考点完全解析(完) 课程预习 1.1 课程内容分为三个模块 基础模块: 技术岗位与面试 计算机基础 JVM原理 多线程 设计模式 数据结构与算法 应用模块: 常用工具集 ...
摘要:看下图所示,摘自网络的创建流程源码分析实例是使用建造者模式通过类进行创建的。创建了一个含有对象实例的,并返回给源码分析添加一个调用适配器工厂,用于支持服务方法返回类型注意生产的是,那么又是什么呢可以看到源代码如下所示,它是一个接口。 目录介绍 1.首先回顾Retrofit简单使用方法 2.Retrofit的创建流程源码分析 2.1 Retrofit对象调用Builder()源码解...
阅读 3014·2021-09-28 09:42
阅读 3390·2021-09-22 15:21
阅读 1101·2021-07-29 13:50
阅读 3472·2019-08-30 15:56
阅读 3352·2019-08-30 15:54
阅读 1177·2019-08-30 13:12
阅读 1123·2019-08-29 17:03
阅读 1155·2019-08-29 10:59