Java RMI 入门指南

本文深入探讨了Java RMI的组成、原理及应用示例,包括如何通过接口定义、实现远程对象、生成桩和框架文件等步骤实现分布式对象间的远程调用。并通过一个简单的Hello世界示例展示了如何在本地或跨平台环境下实现远程方法调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RMI全称是Remote Method Invocation-远程方法调用,Java RMIJDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPCJava版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

RMI目前使用Java远程消息交换协议JRMPJava Remote Messaging Protocol)进行通信由于JRMP是专为Java对象制定的Java RMI具有Java"Write Once,Run Anywhere"的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JREJava Run Environment Java,运行环境)的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。

RMI可利用标准Java本机方法接口JNI与现有的和原有的系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNIRMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。


一、RMI(远程方法调用)的组成

一个正常工作的RMI系统由下面几个部分组成: 

·远程服务的接口定义 
·远程服务接口的具体实现 
·桩(Stub)和框架(Skeleton)文件 
·一个运行远程服务的服务器 
·一个RMI命名服务,它允许客户端去发现这个远程服务 
·类文件的提供者(一个HTTP或者FTP服务器) 
·一个需要这个远程服务的客户端程序 


二、RMI(远程方法调用)原理示意图 



方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传 输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。 

要完成以上步骤需要有以下几个步骤: 

1、 生成一个远程接口 

2、 实现远程对象(服务器端程序)

3、 生成占位程序和骨干网(服务器端程序)

4、 编写服务器程序 

5、 编写客户程序 

6、 注册远程对象 

7、 启动远程对象 

以下用一个最简单的Hello的示例来介绍RMI的应用。

1定义一个远程接口

IHello.java代码如下

import  java.rmi.Remote;

public   interface  IHello  extends  Remote {

public  String sayHello(String name)  throws  java.rmi.RemoteException;

}


2实现远程的接口(服务端就在此远程接口的实现类中)

HelloImpl.java代码如下

import  java.rmi.RemoteException;
import  java.rmi.server.UnicastRemoteObject;
public   class  HelloImpl  extends  UnicastRemoteObject  implements  IHello {
    
//  这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常  
     protected  HelloImpl()  throws  RemoteException {
        
super ();
    }
    
/**
     * 说明清楚此属性的业务含义
     
*/
    
private   static   final   long  serialVersionUID  =   4077329331699640331L ;
    
public  String sayHello(String name)  throws  RemoteException {
        
return   " Hello  "   +  name  +   "  ^_^  " ;
    }
    
public   static   void  main(String[] args) {
        
try  {
            IHello hello 
=   new  HelloImpl();
            java.rmi.Naming.rebind(
" rmi://localhost:1099/hello " , hello);
            System.out.print(
" Ready " );
        } 
catch  (Exception e) {
            e.printStackTrace();
        }
    }
}


3:新建RMI客户端调用程序

Hello_RMI_Client.java代码如下

import  java.rmi.Naming;
public   class  Hello_RMI_Client {
    
public   static   void  main(String[] args) {
        
try  {
            IHello hello 
=  (IHello) Naming.lookup( " rmi://localhost:1099/hello " );
                System.out.println(hello.sayHello(
" zhangxianxin " ));
        } 
catch  (Exception e) {
            e.printStackTrace();
        }
    }
}


4:编译并运行

4.1 javac命令编译IHello.javaHelloImpl.javaHello_RMI_Client.java

>javac *.java

4.2 rmic命令生成桩和框架文件

 >rmic HelloImpl

成功执行完上面的命令可以发现生成一个HelloImpl_stub.class文件,如果JDK是使用Java2SDK,那么还可以发现多出一个HelloImpl_Skel.class文件。如果服务端程序与客户端程序在同一台机器上并在同一目录中,则可以省略掉接口实现类生成的桩和框架文件,但这就失去了使用RMI的意义,而如果要在不同的JVM上运行时,客户端程序就必须得依靠服务端运程方法实现的桩和框架文件以及接口类。

4.3 运行注册程序RMIRegistry,必须在包含刚写的类的目录下运行这个注册程序。

>rmiregistry

注册程序开始运行了,不要管他,现在切换到另外一个控制台运行服务器 

4.4 运行服务器HelloImpl

>java HelloImpl

当启动成功出现Ready...... 这个服务器就开始工作了,把接口的实现加载到内存等待客户端的联接。现在切换到第三个控制台,启动我们的客户端。

4.5 启动客户端:为了在其他的机器运行客户端程序你需要一个远程接口(IHello.class) 和一个stub(HelloImpl_Stub.class)。 使用如下命令运行客户端

>java Hello_RMI_Client

当运行成功会在控制台打印:Hello zhangxianxin ^_^

备注:如果不想在控制台上开启RMI注册程序RMIRegistry的话,可在RMI服务类程序中添加LocateRegistry.createRegistry(1099); 如下所示:

修改后的HelloImpl.java代码如下

import  java.rmi.RemoteException;
import  java.rmi.registry.LocateRegistry;
import  java.rmi.server.UnicastRemoteObject;
public   class  HelloImpl  extends  UnicastRemoteObject  implements  IHello {
    
//  这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常  
     protected  HelloImpl()  throws  RemoteException {
        
super ();
    }
    
    
private   static   final   long  serialVersionUID  =   4077329331699640331L ;
    
public  String sayHello(String name)  throws  RemoteException {
        
return   " Hello  "   +  name  +   "  ^_^  " ;
    }
    
public   static   void  main(String[] args) {
        
try  {
            IHello hello 
=   new  HelloImpl();
            LocateRegistry.createRegistry(
1099 );  // 加上此程序,就可以不要在控制台上开启RMI的注册程序,1099是RMI服务监视的默认端口
            java.rmi.Naming.rebind( " rmi://localhost:1099/hello " , hello);
            System.out.print(
" Ready " );
        } 
catch  (Exception e) {
            e.printStackTrace();
        }
    }
    }


实例

实现简单的查单词功能,一台应用服务器以RMI的方式向客户端提供英译汉词典的服务。

创建一个简单的Java分布式远程方法调用程序可以按以下几个步骤操作:

1)定义远程接口

[java]  view plain copy
  1. /** 
  2.  * 功能说明:定义一个远程接口,必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常  
  3.  * 作者: gangwazi 
  4.  * 创建时间:2012-6-27 
  5.  */  
  6. public interface WordTranslate extends Remote {  
  7.     /** 
  8.      * @param str 需要被翻译的单词 
  9.      * @return 英汉互译后的内容,如果词典中不包含此单词返回null 
  10.      */  
  11.     public String translate(String str) throws RemoteException;  
  12. }  

2)实现远程接口

[java]  view plain copy
  1. /** 
  2.  * 功能说明:远程接口的实现 
  3.  * 作者: gangwazi 
  4.  * 创建时间:2012-6-27 
  5.  */  
  6. public class WordTranslateImpl extends UnicastRemoteObject implements WordTranslate {  
  7.   
  8.     private static final long serialVersionUID = 1L;  
  9.     public Map<String, String> wordMap = new HashMap<String, String>();  
  10.   
  11.     // 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常   
  12.     public WordTranslateImpl() throws RemoteException {  
  13.         super();  
  14.         wordMap.put("China""n. 中国");  
  15.         wordMap.put("Japan""n. 日本");  
  16.         wordMap.put("German""德国");  
  17.         wordMap.put("list""n. 列表; v. 列出");  
  18.         wordMap.put("egg""n. 鸡蛋");  
  19.         wordMap.put("map""n. 地图");  
  20.         wordMap.put("translate""v. 翻译");  
  21.         wordMap.put("banana""n. 香蕉");  
  22.         wordMap.put("apple""n. 苹果");  
  23.         wordMap.put("orange""n. 橘子");  
  24.         wordMap.put("milk""n. 牛奶");  
  25.         wordMap.put("water""n. 水");  
  26.         wordMap.put("drink""v. 喝,饮");  
  27.     }  
  28.   
  29.     @Override  
  30.     public String translate(String str) throws RemoteException {  
  31.         if (wordMap.containsKey(str)) {  
  32.             return wordMap.get(str);  
  33.         } else {  
  34.             return null;  
  35.         }  
  36.     }  
  37.   
  38. }  
由于只是简单的示例,故词典使用map来存储,当然也可以构造专门的词典文件或者从数据库中查询。

3)编写服务器类

[java]  view plain copy
  1. /** 
  2.  * 功能说明:创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中。  
  3.  * 作者: gangwazi 
  4.  * 创建时间:2012-6-27 
  5.  */  
  6. public class WordTranslateServer {  
  7.   
  8.     public static void main(String[] args) {  
  9.         try {  
  10.             // 创建一个远程对象  
  11.             WordTranslate rTranslate = new WordTranslateImpl();  
  12.             // 本地主机上的远程对象注册表Registry的实例,并指定端口为5555,这一步必不可少(Java默认端口是1099),  
  13.             // 必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上   
  14.             LocateRegistry.createRegistry(5555);  
  15.             // 把远程对象注册到RMI注册服务器上,并命名为RTranslate  
  16.             // 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略)  
  17.             Naming.bind("rmi://localhost:5555/RTranslate", rTranslate);  
  18.             System.out.println(">>>>>INFO: 远程WorldTranslate对象绑定成功!");  
  19.         } catch (RemoteException e) {  
  20.             System.out.println("创建远程对象发生异常!");  
  21.             e.printStackTrace();  
  22.         } catch (MalformedURLException e) {  
  23.             System.out.println("发生URL畸形异常!");  
  24.             e.printStackTrace();  
  25.         } catch (AlreadyBoundException e) {  
  26.             System.out.println("发生重复绑定对象异常!");  
  27.             e.printStackTrace();  
  28.         }  
  29.     }  
  30. }  

4)编写客户端类

[java]  view plain copy
  1. /** 
  2.  * 功能说明:客户端测试,在客户端调用远程对象上的远程方法,并返回结果。 
  3.  * 作者: gangwazi 
  4.  * 创建时间:2012-6-27 
  5.  */  
  6. public class WorldTranslateClient {  
  7.   
  8.     public static void main(String[] args) {  
  9.         try {  
  10.             // 在RMI服务注册表中查找名称为RTranslate的对象,并调用其上的方法   
  11.             WordTranslate rTranslate = (WordTranslate) Naming.lookup("rmi://202.117.10.64:5555/RTranslate");  
  12.             System.out.print("查询单词 China----------->");  
  13.             System.out.println(rTranslate.translate("China"));  
  14.             System.out.print("查询单词 list----------->");  
  15.             System.out.println(rTranslate.translate("list"));  
  16.             System.out.print("查询单词 present----------->");  
  17.             System.out.println(rTranslate.translate("present"));  
  18.             System.out.print("查询单词 banana----------->");  
  19.             System.out.println(rTranslate.translate("banana"));  
  20.             System.out.print("查询单词 util----------->");  
  21.             System.out.println(rTranslate.translate("util"));  
  22.             System.out.print("查询单词 drink----------->");  
  23.             System.out.println(rTranslate.translate("drink"));  
  24.         } catch (MalformedURLException e) {  
  25.             // TODO Auto-generated catch block  
  26.             e.printStackTrace();  
  27.         } catch (RemoteException e) {  
  28.             // TODO Auto-generated catch block  
  29.             e.printStackTrace();  
  30.         } catch (NotBoundException e) {  
  31.             // TODO Auto-generated catch block  
  32.             e.printStackTrace();  
  33.         }  
  34.     }  
  35. }  

4. 运行结果

服务器端和客户端分别运行在两台机子上

服务器端运行结果


客户端运行结果




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值