0. 河流,午后,我经过~
《Head First》 第11章 代理模式
之所以写这篇随笔,是因为在看unsafe的时候,看到代理的东西,一下子没有很好的反应过来,这让我有丶不适…
1. 从 远程代理 的样例引入
借助书上的样例引入:现需要通过网络访问远程主机,并获取其数据到本机。
这就带来了一个问题:远程主机里的对象数据,活在与本地主机不同的JVM堆中(地址空间),直接访问不得行!
java.rmi.*包 已经内置了远程调用的功能了
1.底层实现:
客户端对象 需要借助 客户端辅助对象。客户对象 乔装成服务端对象 ,假装自己由客户所需要调用的方法。
服务端对象 从其服务中得到返回值,将它打包,然后运会客户辅助对象(通过SocketIO),客户辅助对象将信息解包,最后将返回值交给客户对象。
2.一些RMI实现细节的补充:
客户端辅助对象 stub(桩)
服务端辅助对象 skeleton(骨架)
JDK中RMI执行stub、skeleton生成的脚本工具rmic
服务端通过Naming.rebind绑定 服务名称&真实的远程服务对象的实例 的映射关系,并且会维护一张注册表(保存这个映射关系)
客户端则通过Naming.lookup("远程ip/服务名称")获取指定的本地代理(stub)
客户获取stub类的方式:
直接移交
dynamic class downloading(url=>stub,这需要有web服务器支持)
jdk5+ => 动态代理替代了RMI中rmic的工作(此时的stub是java.lang.reflect.Proxy的实例)
网络、IO常常会是带有风险的,必须意识到,并且在调用的同时抛出RemoteException
调用方法的签名中变量类型(包括返回类型),必须是primitive原语的(内置的)或者Serializable的
3. 稍微扯一点不大相关的东西...
RMI(Remote Method Invocation)是对RPC(Remote Produce Call)范式的一种Java实现方案
2. 从样例中回到广义的 代理模式
简单的来说:包装一个对象,用于控制对它的访问
与装饰者模式、适配器模式最根本的不同在于——设计意图
使用代理模式的前提不就是:访问者不需要关心背后做事情的真实对象,只要能通过一个约定好的调用方式(一般是由工厂返回一个实现了接口的代理类),然后能把事情解决即可(只不过是通过一个真实对象的"代表"来间接做到的)
3. 远程代理只是其中的一种实现,其实代理模式还存在相当多的变体
也可以说是代理模式的应用场景:
远程代理
前面已经花了很多笔墨了...
虚拟代理
控制开销大的资源的创建
书上通过异步来优化swing组件图片下载等待的样例就很不错
保护代理
根据权限控制不同客户端对同一个资源的访问
书上通过返回不同的代理对象来优化约会app的个人信息类的防篡改
防火墙代理
根据IP、PORT等远程访问的机器信息....
智能引用
常用于统计一个对象被引用的次数
缓存代理
通过缓存大对象供多个客户端共享,优化大开销对象的访问
同步代理
为并发下安全考虑
写入时复制代理
控制对象的复制,只要等到客户真正需要的时候再做复制
虚拟代理的变体
复杂隐藏代理
看起来比较接近外观模式(在一顿复杂的实现前面“挡一层”,并重新暴露一个简单的接口)
两者的区别同理上述两个模式(也就是 随笔的标题)
4. Java API 实现
其实就是前面稍有提及的动态代理(JDK底层是通过反射来实现动态代理的)
声明代理类的实现(不是代理类,只不过是代理类会执行的一个接口) => impl InvocationHandler & override invoke()
(动态)创建代理类 => Proxy.newProxyInstance()
代理类提供了一个静态方法 isProxyClass(),用于暴露代理类的身份,此外代理类一般还会实现一些特定的接口
5. 代理模式 容易带来的 新问题
需要设计的类数目增加