JAVA初窥:RMI

来源:互联网 发布:中国证券软件下载 编辑:程序博客网 时间:2024/06/07 21:58

参考书籍:《Head First Java (中文版)》第二版

Java的远程方法调用 (Remote Method Invocation,RMI)技术,能够帮助我们实现:某一个Java虚拟机上的对象可以调用另一台计算机、另一个Java虚拟机上面的对象的方法,就像调用本地对象的方法一样。

使用RMI时,需要指定协议:JRMP(Java远程消息交换协议JRMP,Java Remote Messaging Protocol)或是IIOP(互联网内部对象请求代理协议,Internet Inter-ORB Protocol)。JRMP是RMI原生的协议,它是为了Java对Java间的远程调用而设计的。另一方面,IIOP是为了CORBA(Common Object Request Broker Architecture)而产生的,它让你能够调用Java对象或其它类型的远程方法。
RMI远程方法调用过程如下图所示:

在RMI中,客户端的辅助设施称为stub,而服务器端的辅助设施称为skeleton。

创建远程服务

创建远程服务包含以下5个步骤:

创建远程服务接口

远程的服务接口要继承java.rmi.Remote,用来定义客户端可以远程调用的方法,stub和skeleton都要实现此接口。它是个作为服务的多态化类,也就是说,客户端会调用实现此接口的stub,而此stub因为会执行网络和输入/输出工作,所以可能会发生各种问题,客户端必须处理或声明异常来认知这一类风险。并且,远程方法的参数和返回值必须都是primitive基本数据类型或Serializable的。
package rmi;import java.rmi.Remote;import java.rmi.RemoteException;/** * 远程服务接口 */public interface IRemoteService extends Remote {public String sayHello() throws RemoteException;}

实现远程服务接口

这是真正执行的类,它实现了定义在远程服务接口中的方法,它是客户端会调用的对象。为了要成为远程服务对象,这个实现类必须要包含与远程有关的功能。其中最简单的方式就是继承java.rim.server.UnicastRemoteObject,以调用父类的远程功能来处理这些工作。UnicastRemoteObject有个小问题:它的构造函数会抛出RemoteException异常,因此需要为你的实现类声明一个构造函数,并在其中抛出RemoteException异常。

package rmi;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;/** * 远程服务实现 */public class RemoteServiceImpl extends UnicastRemoteObject implementsIRemoteService {// 无需声明RemoteException异常public String sayHello() {return "Server syas , 'Hey'";}// 父类的构造函数声明了异常,所以你也得在构造函数中声明异常public RemoteServiceImpl() throws RemoteException {}}

注意:要记得当类被初始化的时候,父类的构造函数一定会被调用,如果父类的构造函数抛出异常,你也得声明你的构造函数会抛出异常。

使用rmic产生stub和skeleton

伴随Java Software Development Kit而来的rmic工具会以远程服务的实现类(不是远程服务接口)产生出两个新的类:stub和skeleton。它会按照命名规则在你的远程服务实现类名称后面加上_Stub或_Skel(JDK 1.2以后将只需要_Stub文件)。

执行rmic命令时需要考虑到包目录结构和完整名称,我的所有示例代码的.class文件均放置在D:\bin\rmi目录下,RemoteServiceImpl类的完整名称为rmi(包路径).RemoteServiceImpl。


由于未指定rmic的生成的目标文件的存放位置,所以会将生成的stub文件和skeleton文件放置在RemoteServiceImpl.class文件所在目录(D:\bin\rmi)下。


启动RMI registry(执行rmiregistry)

rmiregistry就像是电话簿,用户会从此处取得代理(客户端的stub对象),因此需要在远程服务启动前先启动RMI registry。


启动远程服务

远程服务实现类定义完成之后还需要使用java.rmi.Naming的bind()方法 来向RMI registry注册服务,当你注册对象时,RMI系统会把stub加到RMI registry中。

package rmi;import java.rmi.Naming;public class Server {public static void main(String[] args) {try {// 创建出远程对象,然后使用静态的Naming.bind()来产生关联// 所注册的名称会供客户端查询IRemoteService service = new RemoteServiceImpl();Naming.bind("RemoteHello", service);//Naming.rebind("Remote Hello", service);} catch (Exception e) {e.printStackTrace();}}}

客户端如何取得stub对象?

客户端必须取得stub对象,因为客户端必须要调用它的方法,这就得靠RMI registry了。客户端会像查询电话簿一样地搜索,找出上面有相符名称的服务。

RMI会自动将stub解序列化,这就要求客户端在查询服务时一定要有stub类文件,否则将导致stub不能被解序列化。

package rmi;import java.rmi.Naming;public class Client {public static void main(String[] args) {new Client().go();}public void go() {try {//客户端必须使用与服务相同的类型//事实上,客户端不需要知道服务实际上的类型IRemoteService service = (IRemoteService) Naming.lookup("rmi://127.0.0.1/RemoteHello");String s = service.sayHello();System.out.println(s);} catch (Exception e) {e.printStackTrace();}}}

注意事项:

  • 客户端是使用接口来调用stub上的方法,客户端的Java虚拟机必须要有stub类,但客户端不会在程序代码中引用stub类,客户端总是通过接口来操作真正的远程对象。
  • 服务器上必须要有stub和skeleton,以及服务于远程的接口,它会需要stub类是因为stub会被代换成连接在RMI registry上真正的服务。

原创粉丝点击