代理模式
定义:
代理:代表某个真实的对象
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问
种类:
虚拟代理控制访问创建开销大的资源(如hibernate的load方法)
保护代理基于权限控制对资源的访问
远程代理(控制访问远程对象):远程对象的本地代表。远程对象是在不同的地址空间(不同的JVM)运行的远程对象;本地代表(代理)是由本地方法调用的对象,其行为会转发到远程对象。
翻火墙代理,控制网络资源访问保护主图免于“坏客户”侵害。公司翻火墙系统
智能引用代理,当主题被引用时,进行额外的动作。
缓存代理,为开销大的运算提供暂时存储,它也允许多个客户共享结果,以减少计算或网络延迟。web代理服务器,内容管理系统
同步代理,多线程情况下为主题提供安全的访问,分布式环境下对集合对象同步访问控制
复杂隐藏代理(外观代理),隐藏一个类的复杂集合的复杂度,并进行访问控制
写入时复制代理(Copy-On-Write Proxy)控制对象的复制,方法是延迟对象的复制,直到客户真正需要。
类图:
与装饰者模式的区别:
两者看起来像,但是目的不同。装饰者模式是为对象增加行为,包装一个对象并提供额外的行为;而代理模式是控制对象的访问,包装一个对象并控制对它的访问;
与适配器模式的区别:
适配器模式会改变对象适配的接口,包装一个对象并提供不同的接口,而代理模式则实现相同的接口;
与外观模式的区别:
外观模式包装许多对象以简化它们的接口
Java API动态代理(java.lang.reflect)
动态代理:运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你指定的类
注意:JDK代理只能代理实现了接口的类;要代理没实现接口的类,则使用cglib来实现
远程代理(RMI)
客户对象所做的就像是在做远程方法调用(RMI),但其实只是调用本地堆中的“代理”对象上的方法,再由代理处理所有网络通信的低层细节。
调用步骤:
客户端调用客户辅助对象的方法;
客户端辅助对象打包调用信息(变量、方法名称等),并通过网络发送到服务器辅助对象;
服务辅助对象把来自客户辅助对象的信息解包,找到被调用的方法(以及在哪个对象内),调用真正的服务对象的真正方法
服务对象上的方法被调用,并将结果返回给服务辅助对象;
服务辅助对象把调用结果返回信息打包并通过网络发送给客户;
服务辅助对象将返回值解包,返回给客户对象
注意:网络和I/O有风险,随时抛出异常
制作远程服务步骤:
制作远程接口MyRemote(Stub和实际的服务都实现此接口)
public interface MyRemote extends Remote{
public String sayHello() throws RemoteException;
}
返回值将从服务器经过网络运回到客户,必须为基本数据类型、字符串、数组、集合或实现serializable类型的自定义对象
制作远程实现。实现远程接口
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
public String sayHello(){
return "hello , server !"
}
public MyRemoteImpl() throws RemoteException{}
public static void main(String[] args){
try{
MyRemote service = new MyRemoteImpl();
Naming.rebind("RemoteHello",service) ;//在RMI registry中注册此名字和此服务,RMI将服务缓存stub放到registry中
}catch(Exception e){
e.printStackTrace();
}
}
}
产生Stub和Skeleton
在远程实现内上执行rmic,运行rmic MyRemoteImpl后会产生两个辅助对象类(MyRemoteImpl_Stub.class和MyRemoteImpl_Skel.class)
在服务器端能访问到类的目录下,执行remiregistry
启动服务,java MyRemoteImpl
客户从registry中寻找代理(通过lookup服务)
MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello");客户从RMI registry中寻找服务,返回Stub对象(stub反序列化到客户端)
总结
最后,客户端需要(Client.class,MyRemoteImpl_Stub.class,MyRemote.class);
服务器端(MyRemoteImpl.class,MyRemoteImpl_Stub.class,MyRemoteImpl_Skel.class,MyRemote.class)
但是jdk1.5后不再需要stub和skeleton,远程对象的stub被java.lang.reflect.Proxy实例代替了,即不需要rmic了