代理模式之远程代理
远程代理:最经典的代理模式之一,远程代理负责与远程JVM通信,以实现本地调用者与远程被调用者之间的正常交互
有些事情必须得依靠代理来完成,比如要调用另一台机器上的一个方法,我们可能就不得不用代理
远程代理的内部机制是这样的:

解释一下,Stub是“桩”也有人称之为“存根”,代表客服服务对象,也就是所谓的代理,代表了Server对象;
Skeleton是“骨架”,是服务的扶助对象。Stub和Skeleton负责通信,类似于用Socket编写的聊天程序
##步骤:
1、制作远程接口
- 扩展java.rmi.Remote
表示此接口用来支持远程调用 - 声明所有的方法都会抛出RemoteException
因为每次远程调用都是有风险的,所以客户在实现方法的时候要处理异常。 - 确定变量和返回值是属于原语类型或者可序列化类型
也就是说远程方法的变量或者返回值需要io传送,必须是原语或者Serializable类型
packageProxyPattern;import java.rmi.RemoteException;/*** 定义服务接口(扩展自java.rmi.Remote接口)* @author ayqy*/publicinterfaceServiceextends java.rmi.Remote{/* 1.方法返回类型必须是可序列化的Serializable* 2.每一个方法都要声明异常throws RemoteException(因为是RMI方式)* *//*** @return 完整的问候语句* @throws RemoteException*/publicString greet(String name)throwsRemoteException;}
2、制作远程实现(也就是所谓的服务器端的代码)
- 实现远程接口
服务端和客户端都要实现这个接口,保证客户调用正确方法 - 扩展UnicastRemoteObject
要保证你的类具有远程功能就继承该类,让超类帮你做这些工作 - 设计一个不带变量的构造器,并声明RemoteException
超类的问题就是构造器会抛出异常,所以子类需要声明构造器也抛出异常。 - 用RMI Registry注册此服务
为你的服务命名,好让客户在注册表中按照此名字找到,在绑定服务对象的时候,RMI会把服务换成stub,stub放在register中。
packageProxyPattern;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;/*** 实现远程服务(扩展自UnicastRemoteObject并实现自定义远程接口)* @author ayqy*/publicclassMyServiceextendsUnicastRemoteObjectimplementsService{/*** 用来校验程序版本(接收端在反序列化是会验证UID,不符则引发异常)*/privatestaticfinallong serialVersionUID =1L;/*** 空的构造方法,只是为了声明异常(默认的构造方法不会声明异常)* @throws RemoteException*/protectedMyService()throwsRemoteException{}@OverridepublicString greet(String name)throwsRemoteException{return"Hey, "+ name;}}
服务端有了服务还不够,我们需要一个Server帮助我们启动RMI注册服务,并注册远程对象,供客户端调用:
packageProxyPattern;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;/*** 实现服务器类,负责开启服务并注册服务对象* @author ayqy*/publicclassServer{publicstaticvoid main(String[] args){try{//启动RMI注册服务,指定端口为1099 (1099为默认端口)LocateRegistry.createRegistry(1099);//创建服务对象MyService service =newMyService();//把service注册到RMI注册服务器上,命名为MyServiceNaming.rebind("MyService", service);}catch(RemoteException e){// TODO Auto-generated catch blocke.printStackTrace();}catch(MalformedURLException e){// TODO Auto-generated catch blocke.printStackTrace();}}}
3、产生Stub和Skeleton
- 在远程实现类(不是远程接口)上执行rmic
rmic是JDK的工具,主要就是来产生stub和skel两个类的,注意此时这两个类都会以_stub和_skel后缀出现,但stub是需要到客户端,所以等会我们看一下这个类怎么传送到客户端。
直接在CMD中执行
%rmic MyService
4、执行remiregistry
- 开启一个终端,启动rmiregistry
也就是启动注册表,但你要保证可以访问你的类,所以在classes目录下启动比较好
%rmiregistry
5、启动服务
开启一个终端,启动服务
从main中启动,实例化了服务对象并在RMI register中注册
%java Server
6、制作客户端测试
客户端只有拿到了这个stub以后就可以操纵这个服务了
- 客户到RMI registry中寻找
//如果要从另一台启动了RMI注册服务的机器上查找MyService对象,修改IP地址即可Service service =(Service)Naming.lookup("//127.0.0.1:1099/MyService");
- RMI register返回stub对象, stub再返回的时候回被反序列化的,但是首先保证你要有stub类的,这个是因为你实现了同一个接口,所以rmic会自动在客户端产生stub类的。
- 客户调用stub的方法,就像stub就是真正的服务对象一样
packageProxyPattern;/* 参考资料:* 1.JAVA RMI怎么用* http://blog.csdn.net/afterrain/article/details/1819659* 2.RMI内部原理* http://www.cnblogs.com/yin-jingyu/archive/2012/06/14/2549361.html* */import java.rmi.Naming;/*** 实现客户类* @author ayqy*/publicclassClient{/*** 查找远程对象并调用远程方法*/publicstaticvoid main(String[] argv){try{//如果要从另一台启动了RMI注册服务的机器上查找MyService对象,修改IP地址即可Service service =(Service)Naming.lookup("//127.0.0.1:1099/MyService");//调用远程方法System.out.println(service.greet("SmileStone"));}catch(Exception e){System.out.println("Client exception: "+ e);}}}
总结
拦截并控制方法调用(这也是代理模式最大的特点,最典型的,防火墙代理。。) 远程对象的存在对客户是透明的(客户完全把Stub代理对象当做远程对象了,虽然客户有点好奇为什么可能会出现异常。。) 远程代理隐藏了通信细节
当我们需要调用另一台机器(JVM)上指定对象的方法时,使用远程代理是一个不错的选择。。

浙公网安备 33010602011771号