目录:
一、简介
二、RIMI 相关类系统了解
三、RMI 基础实践
四、RMI 与 Spring 实践
一、简介
RMI:Remote Method Invocation(远程方法调用),允许程序调用虚拟机中另一个进程(服务)方法或远程主机上的某个进程(服务)的方法。它是EJB的基础。
RPC:Remote Procedure Call Protocol(远程过程调用协议),和RMI 类似,区别主要有两点:
1、调用方式的差异。RMI 是通过签名(即远程服务类注册时所绑定的访问名称)访问的,而RPC 不需要绑定,是通过类似"类名.方法名"的形式调用的,当匹配到类名与方法名后,即调用执行;
2、适用范围的不同。RMI 是java 语言的一部分,只适用于java 的应用程序。RPC 支持多语言。
上面的理解可能有误,也可理解为RPC 是泛指远程调用框架,RMI 是其Java 语言的实现。一些PRC 框架:RMI 、Hessian、Dubbo。
二、RMI 相关类系统了解
首先看下相关包与类:java.rmi 包
java.rmi.activation包:java1.2起,为远程对象的激活提供支持,即使远程对象的访问是持久的(暂时不作多了解);
java.rmi.dgc包:DGC(distribution garbage collection)类,用于垃圾传送集合算法的服务端;Lease类,请求并授予契约给远程对象,里面包含了VM标识符与契约时间;VMID,虚拟机独特ID。所以此包主要定义了远程访问的时间。
java.rmi.server包:里面我们只关注几个类:RemoteObject 与 RemoteServer抽象类、UnicastRemoteObject 类。
java.registry包:绑定服务相关的类。
从编程步骤理解几个主要的类:
1、Remote 接口(远程服务)
定义远程对象类,即定义我们的远程服务类。
继承关系:UnicastRemoteObject实现类——>继承RemoteServer抽象类——>继承RemoteObject抽象类(实现了Serilazible接口)——>实现了Remote接口(空接口)。
远程对象类(服务类)都需继承Remote 的实现类,常用UnicastRemoteObject 类,但具体的业务逻辑可单独定义在实现了Remote 的接口中(必须直接或间接实现Remote),并在自己的服务类中实现自己的服务接口。
UnicastRemoteObject .java
public class UnicastRemoteObject extends RemoteServer {
//暴露此服务到哪个端口
private int port = 0;
//客户端的Socket工厂(如果有的话)
private RMIClientSocketFactory csf = null;
//服务端的Socket工厂(如果有的话)
private RMIServerSocketFactory ssf = null;
//jdk1.1
private static final long serialVersionUID = 4974527148936298033L;
//暴露此服务,同时设置通信的端口
protected UnicastRemoteObject() throws RemoteException
{
this(0);//端口为0将使用匿名(anonymous)端口1099
}
protected UnicastRemoteObject(int port) throws RemoteException;
//jdk1.2 起
protected UnicastRemoteObject(int port,RMIClientSocketFactory csf,RMIServerSocketFactory ssf)throws RemoteException;
public Object clone() throws CloneNotSupportedException;
@Deprecated
public static RemoteStub exportObject(Remote obj)throws RemoteException;
public static Remote exportObject(Remote obj, int port)throws RemoteException
public static Remote exportObject(Remote obj, int port,RMIClientSocketFactory csf,RMIServerSocketFactory ssf) throws RemoteException;
public static boolean unexportObject(Remote obj, boolean force)throws java.rmi.NoSuchObjectException;
}
2、Registry 接口(注册与绑定远程对象的接口)
绑定某个远程对象到某个地址。
Registry 接口定义:
public interface Registry extends Remote{
//默认注册在1099 端口
public static final int REGISTRY_PORT = 1099;
//lookup
public Remote lookup(String name)throws RemoteException, NotBoundException, AccessException;
//bind
public void bind(String name, Remote obj)throws RemoteException, AlreadyBoundException, AccessException;
//unbind
public void unbind(String name)throws RemoteException, NotBoundException, AccessException;
//rebind
public void rebind(String name, Remote obj)throws RemoteException, AccessException;
//list
public String[] list() throws RemoteException, AccessException;
}
3、LocateRegistry (final 工具类):
用于获得上面的Registry 接口对象。即获得远程对象的与某个主机与端口关联的注册对象。
public final class LocateRegistry{
//此处只展示公有属性与方法
//返回localhost 的1099 端口的注册对象
public static Registry getRegistry();
//返回localhost指定端口的注册对象
public static Registry getRegistry(int port);
//返回某个主机的1099 端口的注册对象
public static Registry getRegistry(String host)throws RemoteException;//端口默认1099
public static Registry getRegistry(String host, int port)throws RemoteException
//还指定通信使用的对象
public static Registry getRegistry(String host, int port, RMIClientSocketFactory csf)throws RemoteException
//在本地创建一个注册对象并暴露,并指定只接受哪个端口的请求。port:仅来源于该port 的请求能访问该注册对象上注册的
//远程服务。暴露过程就类似 UnicastRemoteObject.exportObject(Remote,int)被调用一样
public static Registry createRegistry(int port) throws RemoteException
public static Registry createRegistry(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws
RemoteException
}
3、Naming 工具类(final 类工具类)
上面的LocalRegistry 类用户获得注册对象,而该类用于在注册对象上绑定与获得远程对象(Remote 对象)。
public final class Naming{
//name:URL 格式的名字,返回一个Remote 对象的参考。即在注册机处查找远程服务。
public static Remote lookup(String name) throws NotBoundException,RemoteException;
//name:同上。obj:远程对象。
public static void bind(String name,Remote obj) throws AlreadyBoundException,RemoteException;
//解绑定
public static void unbind(String name)throws NotBoundException,RemoteException;
//重新绑定(重新指为某个名字绑定新的远程对象,该名字对应的所有旧的绑定都将被该绑定替换)
public static void rebind(String name,Remote obj) throws RemoteException,java.net.MalformedURLException;
//查找登记处已绑定的所有名字
public static String[] list(String name) throws RemoteException,java.net.MalformedURLException;
}
说明:
通过源码可知,Naming 类的方法是首先通过传入的name 参数(URL)查找在该URL 主机与端口上注册的注册对象,然后通过调用该注册对象的lookup、unbind 等方法实现将Remote 对象绑定与解绑的。即可通过Naming 发布远程服务,而不需通过Registry 对象。
疑问:UnicastRemoteObject .java 的 exportObject 方法与 Naming 类的bind 方法有什么区别?
前者仅用于将远程对象(服务对象)注册到localhost 的某个端口,而后者除了可以注册到本地某端口,也可注册到某个主机某端口,同时还可进行解绑,重新绑定等操作。
小结:
Remote 接口:提供远程服务的对象;
Registry 接口:绑定远程服务对象的接口;
LocalRegistry 类:创建并暴露与某个主机某个端口绑定的Registry 对象的工具类;
Naming 类:绑定Remote 服务到某个URL(实际还是调用了LocalRegistry 获得Registry ,最终通过Registry 实现绑定的);
UnicastRemoteObject 类:Remote 接口的实现类,所有的远程服务类都必须继承该类或另外一个类(Activatable 类)。该类可以将自己绑定到某个端口,而不需通过Naming 等类。
注:
以URL 形式的参数作为绑定name:http://localhost:1099/remoteObjName
三、RMI 基础实践
四、RMI 与 Spring 实践