RMI是Remote Method Invoke的缩写,是Java面向对象的RPC编程的一个API。利用RMI实现方法回调是很常见的做法。
先简单介绍一下相关类和接口
javax.rmi.Remote
Remote接口用来标识可能被远程虚拟机调用的接口。远程对象必须直接或间接实现这个接口,只有在扩展自Remote的类下声明的方法才可以被远程调用。
javax.rmi.UnicastRemoteObject
UnicastRemoteObject用于按JRMP(Java远程方法协议)导出和回收远程对象,以及获取代理对象。
javax.rmi.LocateRegistry
LocateRegistry用来从特定主机获取一个远程对象的注册表(remote object registry),或是用来在本地特定端口创建一个接受远程调用的远程对象的注册表。(强行翻译API文档)
主要用到一个方法:
static LoacateRegistry createRegistry(int port)
在特定端口上创建一个接受请求的注册表
javax.rmi.Naming
Naming类提供获取和保存远程对象注册表的方法。Naming类中的每个方法都需要一个URL格式的字符串作为name参数,例如//host:port/name。如果URL没有指定端口号,那么Naming类会默认当作1099端口处理。
主要用到两个方法:
方法说明个人注释
static void bind(String name, Remote obj)
绑定一个name和一个远程对象其中URL格式的name指明了主机、端口号和命名空间,所以这个方法其实是在对应端口的注册表上声明一个远程对象的意思
static Remote lookup(String name)
返回一个和name相关联的远程对象的代理根据name给出的URL到远程主机上去找对应的注册表,找到要找的远程对象,然后在本地创建它的代理对象
实现RMI:
首先定义一个继承自Remote的接口,这个接口会被用于server和client端:
import java.rmi.Remote; import java.rmi.RemoteException; public interface IMethod extends Remote { //code参数:用hashCode来标识通信双方来自不同的JVM void sayHi(int code) throws RemoteException; }
然后在服务端实现这个接口:
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class MethodImpl extends UnicastRemoteObject implements IMethod { protected MethodImpl() throws RemoteException { } @Override public void sayHi(int code) throws RemoteException { System.out.println("From " + code + " : Hello"); } }
接着在服务端绑定:
import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; public class Server { public static void main(String[] args) { System.out.println("Runtime hashCode: " + Runtime.getRuntime().hashCode()); try { //直接实例化远程对象 IMethod method = new MethodImpl(); //在10086端口上创建一个远程对象注册表 LocateRegistry.createRegistry(10086); //绑定name和远程对象 Naming.bind("//localhost:10086/method", method); } catch (RemoteException | MalformedURLException | AlreadyBoundException e) { e.printStackTrace(); } } }
最后在客户端调用:
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; public class Client { public static void main(String[] args){ System.out.println("Runtime hashCode: " + Runtime.getRuntime().hashCode()); try { //获取远程对象代理 IMethod method = (IMethod) Naming.lookup("//localhost:10086/method"); //调用远程过程 method.sayHi(Runtime.getRuntime().hashCode()); } catch (NotBoundException | MalformedURLException | RemoteException e) { e.printStackTrace(); } } }
先运行起服务端程序,再运行客户端
可以看到在服务端的控制台输出了两行
Runtime hashCode : 服务端hashCode
From 客户端hashCode : Hello
同时服务端hashCode和客户端hashCode是不一样的,说明客户端成功地调用了服务端的sayHi方法。
回调方法会在明天继续 ......