友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第188章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






15。8 远程方法  



为通过网络执行其他机器上的代码,传统的方法不仅难以学习和掌握,也极易出错。思考这个问题最佳的方 

式是:某些对象正好位于另一台机器,我们可向它们发送一条消息,并获得返回结果,就象那些对象位于自 

己的本地机器一样。Java 1。1 的“远程方法调用”(RMI)采用的正是这种抽象。本节将引导大家经历一些 

必要的步骤,创建自己的RMI 对象。  



15。8。1 远程接口概念  



RMI 对接口有着强烈的依赖。在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细 

节。所以客户得到远程对象的一个句柄时,它们真正得到的是接口句柄。这个句柄正好同一些本地的根代码 

连接,由后者负责通过网络通信。但我们并不关心这些事情,只需通过自己的接口句柄发送消息即可。  

创建一个远程接口时,必须遵守下列规则:  

(1) 远程接口必须为public 属性(不能有“包访问”;也就是说,它不能是“友好的”)。否则,一旦客户 



                                                                            582 


…………………………………………………………Page 584……………………………………………………………

试图装载一个实现了远程接口的远程对象,就会得到一个错误。  

(2) 远程接口必须扩展接口 java。rmi。Remote。  

(3) 除与应用程序本身有关的违例之外,远程接口中的每个方法都必须在自己的throws 从句中声明 

java。rmi。RemoteException。  

(4) 作为参数或返回值传递的一个远程对象(不管是直接的,还是在本地对象中嵌入)必须声明为远程接 

口,不可声明为实施类。  

  

下面是一个简单的远程接口示例,它代表的是一个精确计时服务:  

  

//: PerfectTimeI。java  

// The PerfectTime remote interface  

package c15。ptime;  

import java。rmi。*;  

  

interface PerfectTimeI extends Remote {  

  long getPerfectTime() throws RemoteException;  

} ///:~  

  

它表面上与其他接口是类似的,只是对Remote 进行了扩展,而且它的所有方法都会“掷”出 

RemoteException (远程违例)。记住接口和它所有的方法都是public 的。  



15。8。2 远程接口的实施  



服务器必须包含一个扩展了UnicastRemoteObject 的类,并实现远程接口。这个类也可以含有附加的方法, 

但客户只能使用远程接口中的方法。这是显然的,因为客户得到的只是指向接口的一个句柄,而非实现它的 

那个类。  

必须为远程对象明确定义构建器,即使只准备定义一个默认构建器,用它调用基础类构建器。必须把它明确 

地编写出来,因为它必须“掷”出 RemoteException 违例。  

下面列出远程接口PerfectTime 的实施过程:  

  

//: PerfectTime。java  

// The implementation of the PerfectTime   

// remote object  

package c15。ptime;  

import java。rmi。*;  

import java。rmi。server。*;  

import java。rmi。registry。*;  

import java。*;  

  

public class PerfectTime   

    extends UnicastRemoteObject  

    implements PerfectTimeI {  

  // Implementation of the interface:  

  public long getPerfectTime()   

      throws RemoteException {  

    return System。currentTimeMillis();  

  }  

  // Must implement constructor to throw  

  // RemoteException:  

  public PerfectTime() throws RemoteException {  

    // super(); // Called automatically  

  }  

  // Registration for RMI serving:  



                                                                                          583 


…………………………………………………………Page 585……………………………………………………………

  public static void main(String'' args) {  

    System。setSecurityManager(  

      new RMISecurityManager());  

    try {  

      PerfectTime pt = new PerfectTime();  

      Naming。bind(  

        〃//colossus:2005/PerfectTime〃; pt);  

      System。out。println(〃Ready to do time〃);  

    } catch(Exception e) {  

      e。printStackTrace();  

    }  

  }  

} ///:~  

  

在这里,main()控制着设置服务器的全部细节。保存RMI 对象时,必须在程序的某个地方采取下述操作:  

(1) 创建和安装一个安全管理器,令其支持RMI。作为Java 发行包的一部分,适用于RMI 唯一一个是 

RMISecurityManager 。  

(2) 创建远程对象的一个或多个实例。在这里,大家可看到创建的是PerfectTime 对象。  

(3) 向RMI 远程对象注册表注册至少一个远程对象。一个远程对象拥有的方法可生成指向其他远程对象的句 

柄。这样一来,客户只需到注册表里访问一次,得到第一个远程对象即可。  

  

1。 设置注册表  

在这儿,大家可看到对静态方法Naming。bind() 的一个调用。然而,这个调用要求注册表作为计算机上的一 

个独立进程运行。注册表服务器的名字是 rmiregistry。在32 位Windows 环境中,可使用:  

start rmiregistry  

令其在后台运行。在Unix 中,使用:  

rmiregistry &  

和许多网络程序一样,rmiregistry 位于机器启动它所在的某个 IP 地址处,但它也必须监视一个端口。如果 

象上面那样调用rmiregistry,不使用参数,注册表的端口就会默认为 1099。若希望它位于其他某个端口, 

只需在命令行添加一个参数,指定那个端口编号即可。对这个例子来说,端口将位于 2005,所以 

rmiregistry 应该象下面这样启动(对于32 位Windows):  

start rmiregistry 2005  

对于Unix ,则使用下述命令:  

rmiregistry 2005 &  

与端口有关的信息必须传送给bind()命令,同时传送的还有注册表所在的那台机器的 IP 地址。但假若我们 

想在本地测试RMI 程序,就象本章的网络程序一直测试的那样,这样做就会带来问题。在 JDK 1。1。1 版本 

中,存在着下述两方面的问题(注释⑦):  

(1) localhost不能随 RMI 工作。所以为了在单独一台机器上完成对 RMI 的测试,必须提供机器的名字。为 

了在32 位 Windows 环境中调查自己机器的名字,可进入控制面板,选择“网络”,选择“标识”卡片,其中 

列出了计算机的名字。就我自己的情况来说,我的机器叫作“Colossus ”(因为我用几个大容量的硬盘保存 

各种不同的开发系统——Clossus 是“巨人”的意思)。似乎大写形式会被忽略。  

(2) 除非计算机有一个活动的TCP/IP 连接,否则 RMI 不能工作,即使所有组件都只需要在本地机器里互相通 

信。这意味着在试图运行程序之前,必须连接到自己的 ISP (因特网服务提供者),否则会得到一些含义模 

糊的违例消息。  

  

⑦:为找出这些信息,我不知损伤了多少个脑细胞。  

  

考虑到这些因素,bind()命令变成了下面这个样子:  

Naming。bind(〃//colossus:2005/PerfectTime〃; pt);  

若使用默认端口 1099,就没有必要指定一个端口,所以可以使用:  

Naming。bind(〃//colossus/PerfectTime〃; pt);  

在JDK 未来的版本中(1。1之后),一旦改正了 localhost 的问题,就能正常地进行本地测试,去掉 IP地 



                                                                                   584 


…………………………………………………………Page 586……………………………………………………………

址,只使用标识符:  

Naming。bind(〃PerfectTime〃; pt);  

服务名是任意的;它在这里正好为 PerfectTime,和类名一样,但你可以根据情况任意修改。最重要的是确 

保它在注册表里是个独一无二的名字,以便客户正常地获取远程对象。若这个名字已在注册表里了,就会得 

到一个AlreadyBoundException 违例。为防止这个问题,可考虑坚持使用 rebind() ,放弃bind()。这是由于 

rebind()要么会添加一个新条目,要么将同名的条目替换掉。  

尽管main()退出,我们的对象已经创建并注册,所以会由注册表一直保持活动状态,等候客户到达并发出对 

它的请求。只要rmiregistry 处于运行状态,而且我们没有为名字调用 Naming。unbind()方法,对象就肯定 

位于那个地方。考虑到这个原因,在我们设计自己的代码时,需要先关闭 rmiregistry,并在编译远程对象 

的一个新版本时重新启动它。  

并不一定要将rmiregistry 作为一个外部进程启动。若事前知道自己的是要求用以注册表的唯一一个应用, 

就可在程序内部启动它,使用下述代码:  

LocateRegistry。createRegistry(2005);  

和前面一样,2005 代表我们在这个例子里选用的端口号。这等价于在命令行执行 rmiregistry 2005。但在设 

计RMI 代码时,这种做法往往显得更加方便,因为它取消了启动和中止注册表所需的额外步骤。一旦执行完 

这个代码,就可象以前一样使用Naming 进行“绑定”——bind()。  



15。8。3 创建根与干  



若编译和运行PerfectTime。java,即使rmiregistry 正确运行,它也无法工作。这是由于RMI 的框架尚未就 

位。首先必须创建根和干,以便提供网络连接操作,并使我们将远程对象伪装成自己机器内的某个本地对 

象。  

所有这些幕后的工作都是相当复杂的。我们从远程对象传入、传出的任何对象都必须“implement  

Serializable”(如果想传递远程引用,而非整个对象,对象的参数就可以“implement Remote”)。因此 

可以想象,当根和干通过网络“汇集”所有参数并返回结果的时候,会自动进行序列化以及数据的重新装 

配。幸运的是,我们根本没必要了解这些方面的任何细节,但根和干却是必须创建的。一个简单的过程如 

下:在编译好的代码中调用rmic,它会创建必需的一些文件。所以唯一要做的事情就是为编译过程新添一个 

步骤。  

然而,rmic 工具与特定的包和类路径有很大的关联。PerfectTime。java 位于包 c15。Ptime 中,即使我们调用 

与PerfectTime。class 同一目录内的rmic,rmic 都无法找到文件。这是由于它搜索的是类路径。因此,我们 

必须同时指定类路径�
返回目录 上一页 下一页 回到顶部 赞(0) 踩(0)
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!