RMI(即Remote Method Invok远程方法调用)。在Java中,只要一个类实现了java.rmi.Remote接口,即可成为存在于服务器端的远程对象,供客户端访问并提供一定的服务。JavaDoc描述
:Remote接口用于标识其方法可以从非本地虚拟上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”(扩展java.rmi.Remote的接口)中指定的这些方法才可远程使用。
注意:extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常,则表明该方法可悲客户端远程访问调用
同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”,而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。
下面给出一个简单的RMI应用,其中类图如下:其中IService接口用于声明服务器端必须提供的服务(即service()方法),ServiceImpl类是具体的服务实现类,而Server类最终负责注册服务器远程对象,以便在服务器段存在骨架代理对象来对客户端的请求提供处理和响应。
各个类的源代码如下:
IService接口:
importjava.rmi.Remote;
importjava.rmi.RemoteException;
publicinterfaceIServiceextendsRemote {
//声明服务器端必须提供的服务
String service(String content)throwsRemoteException;
}
ServiceImpl实现类:
importjava.rmi.RemoteException;
//UnicastRemoteObject用于导出的远程对象和获得与该远程对象通信的存根。
importjava.rmi.server.UnicastRemoteObject;
publicclassServiceImplextendsUnicastRemoteObjectimplementsIService {
privateString name;
publicServiceImpl(String name)throwsRemoteException {
this.name = name;
}
@Override
publicString service(String content) {
return"server >> "+ content;
}
}
Server类:
/*
* Context接口表示一个命名上下文,它由一组名称到对象的绑定组成。
* 它包含检查和更新这些绑定的一些方法。
*/
importjavax.naming.Context;
/*
* InitialContext类是执行命名操作的初始上下文。
* 该初始上下文实现 Context 接口并提供解析名称的起始点。
*/
importjavax.naming.InitialContext;
publicclassServer {
publicstaticvoidmain(String[] args) {
try{
//实例化实现了IService接口的远程服务ServiceImpl对象
IService service02 =newServiceImpl("service02");
//初始化命名空间
Context namingContext =newInitialContext();
//将名称绑定到对象,即向命名空间注册已经实例化的远程服务对象
namingContext.rebind("rmi://localhost/service02", service02);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("服务器向命名表注册了1个远程服务对象!");
}
}
Client类:
importjavax.naming.Context;
importjavax.naming.InitialContext;
publicclassClient {
publicstaticvoidmain(String[] args) {
String url ="rmi://localhost/";
try{
Context namingContext =newInitialContext();
// 检索指定的对象。 即找到服务器端相对应的服务对象存根
IService service02 = (IService) namingContext.lookup(url
+"service02");
Class stubClass = service02.getClass();
System.out.println(service02 +" 是 "+ stubClass.getName()
+" 的实例!");
// 获得本底存根已实现的接口类型
Class[] interfaces = stubClass.getInterfaces();
for(Class c : interfaces) {
System.out.println("存根类实现了 "+ c.getName() +" 接口!");
}
System.out.println(service02.service("你好!"));
}catch(Exception e) {
e.printStackTrace();
}
}
}