1. 概述
当两个JVM需要通信时,Java RMI是可选方案之一。本文将通过一个简单示例演示Java RMI技术的核心用法。
2. 创建服务器
创建RMI服务器需要两个步骤:
- 定义客户端/服务器通信的接口
- 实现该接口
2.1. 定义契约
首先创建远程对象接口,该接口需继承java.rmi.Remote
标记接口。接口中声明的每个方法都必须抛出java.rmi.RemoteException
:
public interface MessengerService extends Remote {
String sendMessage(String clientMessage) throws RemoteException;
}
⚠️ 注意:RMI支持完整的Java方法签名规范,但参数类型必须实现java.io.Serializable
。
后续章节将说明客户端和服务器如何使用此接口:
- 服务器端需创建实现类(称为远程对象)
- 客户端则由RMI库动态生成实现(称为存根Stub)
2.2. 实现
实现远程接口(即远程对象):
public class MessengerServiceImpl implements MessengerService {
@Override
public String sendMessage(String clientMessage) {
return "Client Message".equals(clientMessage) ? "Server Message" : null;
}
public String unexposedMethod() { /* code */ }
}
✅ 实现要点:
- 方法声明中省略了
throws RemoteException
(远程对象通常不直接抛出此异常) - 保持实现类与RMI解耦
- 接口中未声明的方法(如
unexposedMethod
)对客户端不可见
3. 注册服务
创建远程实现后,需将远程对象绑定到RMI注册表。
3.1. 创建存根
首先生成远程对象的存根:
MessengerService server = new MessengerServiceImpl();
MessengerService stub = (MessengerService) UnicastRemoteObject
.exportObject((MessengerService) server, 0);
UnicastRemoteObject.exportObject
负责创建存根实现,存根通过底层RMI协议实现与服务器通信。
参数说明:
- 第一个参数:远程服务器对象
- 第二个参数:导出端口(0表示动态选择端口)
❌ 注意:无端口号的exportObject()
方法已废弃。
3.2. 创建注册表
注册表可本地创建或作为独立服务运行。这里采用本地创建:
Registry registry = LocateRegistry.createRegistry(1099);
✅ 关键点:
- 默认端口1099(可在
createRegistry
中指定其他端口) - 服务器本地创建使用
createRegistry
- 独立服务场景需用
getRegistry
并传入主机名和端口
3.3. 绑定存根
将存根绑定到注册表(类似JNDI的命名服务):
registry.rebind("MessengerService", stub);
绑定后,任何能访问注册表的客户端都可使用该远程对象。
4. 创建客户端
客户端调用远程方法的步骤:
- 定位RMI注册表
- 通过唯一键查找远程对象存根
- 调用远程方法
Registry registry = LocateRegistry.getRegistry();
MessengerService server = (MessengerService) registry
.lookup("MessengerService");
String responseMessage = server.sendMessage("Client Message");
String expectedMessage = "Server Message";
assertEquals(expectedMessage, responseMessage);
✅ 执行说明:
- 因注册表在本地且使用默认端口1099,
getRegistry
无需参数 - 若注册表在其他主机/端口,需传入相应参数
- 获取存根后即可像本地对象一样调用方法
5. 总结
本文简要介绍了Java RMI的基础用法,展示了其作为客户端-服务器应用框架的核心能力。后续将深入探讨RMI的更多高级特性。
完整源码请参考:GitHub仓库