初识JavaRMI原理(转)
RMI(即Remote Method Invoke 远程方法调用)。在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象,供客户端访问并提供一定的服务。JavaDoc描述:Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的这些方法才可远程使用。
注意:extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常,则表明该方法可被客户端远程访问调用。
同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”,而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。
RMI 框架的基本原理大概如下图,应用了代理模式来封装了本地存根与真实的远程对象进行通信的细节。
下面给出一个简单的RMI 应用,其中类图如下:其中IService接口用于声明服务器端必须提供的服务(即service()方法),ServiceImpl类是具体的服务实现类,而Server类是最终负责注册服务器远程对象,以便在服务器端存在骨架代理对象来对客户端的请求提供处理和响应。
各个类的源代码如下:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IService extends Remote {
// 声明服务器端必须提供的服务
public String service(String content) throws RemoteException;
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//UnicastRemoteObject用于导出的远程对象和获得与该远程对象通信的存根。
public class ServiceImpl extends UnicastRemoteObject implements IService {
private String name;
protected ServiceImpl(String name) throws RemoteException {
this.name = name;
}
@Override
public String service(String content) throws RemoteException {
System.out.println(content + " --------- 我被客户端调用了 ---------");
return "server------>" + content;
}
}
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class Server {
public static void main(String[] args) {
try {
// 实例化实现了IService接口的远程服务ServiceImpl对象
IService service01 = new ServiceImpl("service01");
// 初始化命名空间
Context ct = new InitialContext();
LocateRegistry.createRegistry(8888);
// 将名称绑定到对象,即向命名空间注册已经实例化的远程服务对象
ct.rebind("rmi://127.0.0.1:8888/service01", service01);
System.out.println(">>>>>INFO:远程service对象绑定成功!>>>>>");
} catch (RemoteException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
}
import javax.naming.Context;
import javax.naming.InitialContext;
public class Client {
public static void main(String[] args) {
String url = "rmi://localhost:8888/";
try {
Context ct = new InitialContext();
// 检索指定的对象 即找到服务器端相对应的服务对象存根
IService service = (IService) ct.lookup(url + "service01");
System.out.println(service + " is :" + service.getClass().getName()
+ " instance");
Class<?>[] sInterfaces = service.getClass().getInterfaces();
for (Class<?> clazz : sInterfaces) {
System.out.println(" interface is " + clazz.getName());
}
String str = service.service("你好");
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
其实整个简单的RMI 应用中各个类的交互时序如下图:
推荐一篇更完善的文章:http://ryxxlong.iteye.com/blog/1560535