java 2 enterprise edition(j2ee)远程方法调用(remote method invocation,rmi)框架允许你创建透明的、分布式的服务和应用程序。基于rmi的应用程序由java对象构成,这些对象相互调用,同时忽略对方的位置。换言之,一个java对象可调用另一个虚拟机上的某个java对象的方法,整个过程和调用同一个虚拟机上的某个java对象的方法无异。
驻留在不同虚拟机上的对象为了相互获得引用,可以使用rmi的查找服务,或者将对象引用作为方法调用的一个参数或者返回值来接收。参数和返回值借助java的对象序列化机制由rmi来进行封送。
远程对象和接口
java提供了一个完全限定名称为java.rmi.remote的接口。任何对象要想参与和另一个java对象的远程会话,就必须直接或间接地实现该接口。尤其要注意的是,任何由java.rmi.remote接口来标识的对象都暗示着它的方法可从其他任何虚拟机进行调用。实现了java.rmi.remote接口的对象通常称为“远程对象”,必须采用以下方式来声明它的方法:
每个支持远程调用的方法都必须在其throws子句中声明java.rmi.remoteexception。
对于一个可远程调用的方法,它的每个非基本(nonprimitive)参数或者返回值都必须直接或间接地声明为实现了java.io.serializable接口。
除了实现java.rmi.remote接口和正确声明任何远程方法之外,远程对象必须提供一个无参数的构造函数,它能引发一个java.rmi.remoteexception异常。这就保证了对象可基于一种序列化状态来远程构造。
远程对象必须导出,以接收传入的远程方法调用。为此,你通常需要扩展java.rmi.server.unicastremoteobject或者java.rmi.activation.activatable。通过对其中任何一个类进行扩展,远程对象就可在创建时自动导出。
以下接口定义展示了java.rmi.remote接口最典型的用法:
import java.rmi.remote;
import java.rmi.remoteexception;
public interface timekeeper extends remote
{
public string currentdate() throws remoteexception;
public string currenttime() throws remoteexception;
}
由于string类声明为实现了java.io.serializable接口,所以string是远程方法的有效返回类型。
以下代码展示了如何实现timekeeper接口,以便定义一个有效的远程对象:
import java.rmi.remoteexception;
import java.util.calendar;
import java.util.gregoriancalendar;
public class timekeeperimpl implements timekeeper
{
public timekeeperimpl()
throws remoteexception
{
}
public string currentdate() throws remoteexception
{
calendar cal = new gregoriancalendar();
string retval = (cal.get(calendar.month) + "/" +
cal.get(calendar.day_of_month) + "/" +
cal.get(calendar.year));
return retval;
}
public string currenttime() throws remoteexception
{
calendar cal = new gregoriancalendar();
string retval = (cal.get(calendar.hour_of_day) + ":" +
cal.get(calendar.minute) + ":" +
cal.get(calendar.second));
return retval;
}
}
rmi注册表
为了获取对远程对象的引用,rmi提供了名为注册表(registry)的一个远程对象,它将名称与远程对象关联起来。rmi服务器要向注册表注册每一个远程对象,以便定位和检索对象。rmi客户端希望调用远程对象上的一个方法时,首先必须根据远程对象的名称在注册表中定位远程对象。如果远程对象存在,注册表就返回对那个对象的一个引用。然后,要使用这个引用来发出对远程对象的方法调用。
rmi服务器
rmi采取一种客户机/服务器结构进行通信。这意味着在rmi会话的某一端,必须有一个对象充当服务器,另一端的对象则充当客户端。rmi服务器负责创建每个远程对象的实例,并将每个实例和rmi注册表中的一个名称绑定起来。rmi服务器可以自主,这要求它实现一个main方法,避免必须依赖其他类才能执行。
由于rmi服务器可从几乎任何主机下载和执行代码,所以每个rmi服务器的main方法都需要安装一个安全管理器,防止它所加载的类表现失常。下例展示了如何实例化一个安全管理器,以及如何在rmi注册表中绑定一个对象实例:
import java.rmi.rmisecuritymanager;
import java.rmi.naming;
public class simplermiserver
{
public static void main(string[] args)
{
if (system.getsecuritymanager() == null)
{
system.setsecuritymanager(new rmisecuritymanager());
}
try
{
timekeeperimplremoteobj = new timekeeperimpl();
// bind the remote object to the name "timekeeper"
naming.bind("//hostname/timekeeper", remoteobj);
system.out.println("timekeeper successfully bound in registry");
}
catch (exception e)
{
system.err.println("error binding timekeeper: " + e.getmessage());
}
}
}
小结
本文简单介绍了如何用rmi来隐藏远程交互问题,使程序员能将注意力集中在其他更重要的问题上,而不必过多地考虑通信基础结构。下一篇文章将进一步探索rmi,讲解rmi客户端如何定位远程对象,并调用其上的方法。
闽公网安备 35060202000074号