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;
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 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