RMI的实现
远程方法调用基本流程
远程方法,可以让本地访问远程的对象。当需要访问远程对象的时候,一般需要在客户端或者客户堆中建立一些辅助对象,这些辅助对象使得客户感觉如同调用本地对象的方法一样。客户辅助对象乔装为服务对象,当客户调用客户辅助对象上的方法,客户辅助对象会联系服务器,传送方法调用信息(方法名称,变量等),然后就等待服务器的返回。
在服务器端,服务辅助对象从客户辅助对象中接受请求(Socket连接),将调用的信息解包,然后调用服务器端上的真正服务对象的真正方法。对于服务对象来说,调用是本地的。
服务辅助对象从服务中得到返回值,将它打包,然后就运回到客户辅助对象,客户辅助对象对信息解包,最后将返回值交给客户对象。
RMI概况
RMI提供了客户辅助对象和服务辅助对象,为客户辅助对象和服务对象创建相同的方法。由于调用远程方法的过程中使用的是I/O和网络,而这些都有可能发生异常。
RMI将客户辅助对象成为stub(桩),服务辅助对象称为skeleton(骨架)。
制作远程服务
步骤一 制作远程接口:定义了可以供客户远程调用的方法。stub和实际服务都实现该接口
步骤二 制作远程接口的实现:真正的实际工作的服务
步骤三 利用rmic产生stub
步骤四 启动RMI:registry,rmiregistry如同一个清单,客户可以从中查到代理的位置
步骤五 开始启动远程服务:开始启动远程服务,一般服务实现类会去实例化一个服务实例,然后将这个服务注册到RMI registry。
步骤一 制作远程接口
一般首先扩展自Remote,Remote仅仅是一个记号接口,没有方法,在该接口中声明所有的方法都会抛出RemoteException,必须确定变量和返回值是原语性或者可序列化的,如果是复杂对象则需要对于负责对象先序列化。即让该复杂对象继承Serializable。
步骤二 制作远程接口的实现
具体的服务必须实现远程接口,同时一般直接扩展UnicastRemoteObject,设计一个不带变量的构造器,同时声明
RemoteException。(当类被实例化的时候,超类的构造器总是先被调用,如果超类的构造器抛出了异常,那么在子类的构造器中必须抛出异常,而
UnicastRemoteObject的构造器抛出了异常。)可以在实现类中注册该服务,但是必须要先启动
rmiregistry。 Naming.rebind("name",myremoteImpl)注册到
rmiregistry中。
步骤三 产生stub
rmic 远程实现类名
步骤四 执行
rmiregistry
步骤五 启动服务
启动服务,一般可以在实现类的内部启动或者单独类中启动,主要就是注册到
rmiregistry
在远程虚拟机上启动服务器一般要包括两个服务器,一个是远程对象本身,还有一个是允许本地客户端下载远程对象引用的注册表。必须要先执行
rmiregistry目的开启注册表,然后再启动远程对象注册到该注册表中
以上是服务器端的配置,在客户端,只需要该远程对象接口的类或者.class文件,以及利用rmic生成的stud文件。
代码实例
远程接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyRemote extends Remote {
public String sayHelloBaby() throws RemoteException;
}
import java.rmi.RemoteException;
public interface MyRemote extends Remote {
public String sayHelloBaby() throws RemoteException;
}
远程接口实现
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException {
}
@Override
public String sayHelloBaby() throws RemoteException {
// TODO Auto-generated method stub
return "Congratulations to you!";
}
}
import java.rmi.server.UnicastRemoteObject;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException {
}
@Override
public String sayHelloBaby() throws RemoteException {
// TODO Auto-generated method stub
return "Congratulations to you!";
}
}
启动远程服务,必须先执行rmiregistry,
在windows下执行start rmiregistry
import java.io.IOException;
import java.rmi.Naming;
public class RMIServer {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
MyRemote f= new MyRemoteImpl();
Naming.rebind( "myfirst", f);
System.out.println( "OK");
} catch(IOException e)
{
System.err.println(e);
}
}
}
import java.rmi.Naming;
public class RMIServer {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
MyRemote f= new MyRemoteImpl();
Naming.rebind( "myfirst", f);
System.out.println( "OK");
} catch(IOException e)
{
System.err.println(e);
}
}
}
客户端服务
import java.rmi.Naming;
public class MyRemoteClient {
public static void main(String[] args) {
// TODO Auto-generated method stub
try
{
MyRemote service=(MyRemote)Naming.lookup( "rmi://127.0.0.1/FirstRemote");
String res=service.sayHelloBaby();
System.out.println("return="+res);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
public class MyRemoteClient {
public static void main(String[] args) {
// TODO Auto-generated method stub
try
{
MyRemote service=(MyRemote)Naming.lookup( "rmi://127.0.0.1/FirstRemote");
String res=service.sayHelloBaby();
System.out.println("return="+res);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
整个目录截图如下
客户端所需要类:F:\JavaRMI\client
服务器所需要类:F:\JavaRMI\server
执行顺序是:
第一步:编译所有的类,在此要将远程对象接口复制到客户端下
第二步:执行rmic MyRemoteImpl,这个目的生成桩字节码
第三步:将该桩字节码复制一份到客户端目录下
第四步:启动rmiregistry服务,必须要保证桩字节码在classpath路径下
本次桩字节码在F:\JavaRMI\server,所以启动rmiregistry之前先设置路径
第五步:启动远程对象服务即RMIServer
第六步:执行客户端程序
在使用RMI的过程中,必须要注意:
1) 在启动远程服务之前先必须要启动rmiregistry,必须要保证桩在类路径中,
可以在启动
rmiregistry之前设置
2)必须要让变量的返回值的类型成为可序列化的类型
3)客户端必须要有Stub类和远程对象接口类。
问题解决:
在执行服务器远程对象运行的中,可能会出现
java.rmi.ServerException: RemoteException occurred in server thread; nested
exception is:
这是因为
在执行start rmiregistry 命令前必须确保 MyRemoteImpl_Stub.class(留意包
名)包含在了CLASSPATH配置的路径中,所以必须在
在执行start rmiregistry命令前执行
set CLASSPATH=%CLASSPATH%;F:
\JavaRMI\server
本文转自 zhao_xiao_long 51CTO博客,原文链接:http://blog.51cto.com/computerdragon/1178053