RMI (Remote Method Invocation) 是 Java 內建的分散式物件框架,讓跨 JVM 物件調用如同本地調用般透明。本文系統解析 RMI 核心機制、五步驟實作流程、關鍵類別、安全模型與完整代碼範例。
| 類別/介面 | 套件 | 角色 |
Remote | java.rmi | 標記介面,遠端物件必須實作 |
UnicastRemoteObject | java.rmi.server | 遠端物件基類,提供單點連線實作 |
Naming | java.rmi | 註冊表查找/綁定:lookup/bind/rebind |
Registry | java.rmi.registry | 名稱服務,預設埠 1099 |
RemoteException | java.rmi | 所有遠端方法必須拋出的檢查型異常 |
五步驟建立 RMI 應用
步驟 1:定義遠端介面
package com.example.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloService extends Remote {
String sayHello(String name) throws RemoteException;
int add(int a, int b) throws RemoteException;
}
- 介面必須
public、繼承 Remote
- 所有方法拋出
RemoteException
- 參數/返回值必須可序列化或為遠端介面
步驟 2:實作遠端介面
package com.example.rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
protected HelloServiceImpl() throws RemoteException { super(); }
@Override
public String sayHello(String name) throws RemoteException {
return "Hello, " + name + "! Server time: " + new java.util.Date();
}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
- 繼承
UnicastRemoteObject 獲得遠端能力
- 建構子必須拋出
RemoteException
步驟 3:生成 Stub/Skeleton (JDK 5+ 可略過)
rmic com.example.rmi.HelloServiceImpl
JDK 5+ 使用動態 Proxy 替代預生成 Stub,rmic 僅在需相容舊版客戶端時必要。
步驟 4:編寫伺服端啟動類
package com.example.rmi;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class Server {
public static void main(String[] args) {
try {
// 1. 建立並啟動 Registry (埠 1099)
LocateRegistry.createRegistry(1099);
// 2. 實例化遠端物件
HelloService service = new HelloServiceImpl();
// 3. 綁定名稱至 Registry
Naming.rebind("rmi://localhost:1099/HelloService", service);
System.out.println("RMI Server ready: rmi://localhost:1099/HelloService");
} catch (Exception e) {
e.printStackTrace();
}
}
}
步驟 5:編寫客戶端調用類
package com.example.rmi;
import java.rmi.Naming;
public class Client {
public static void main(String[] args) {
try {
// 1. 從 Registry 查找遠端物件
HelloService service = (HelloService) Naming.lookup("rmi://localhost:1099/HelloService");
// 2. 像本地調用一樣呼叫遠端方法
System.out.println(service.sayHello("RMI Learner"));
System.out.println("10 + 20 = " + service.add(10, 20));
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行順序與驗證
- 編譯所有類別:
javac com/example/rmi/*.java
- 啟動伺服端:
java com.example.rmi.Server
- 啟動客戶端:
java com.example.rmi.Client
- 觀察客戶端輸出遠端方法執行結果
進階主題
安全管理器與代碼下載
// 伺服端啟動參數
java -Djava.security.policy=server.policy \
-Djava.rmi.server.codebase=file:/path/to/classes/ \
com.example.rmi.Server
server.policy 範例:
grant {
permission java.security.AllPermission;
};
自定義 Socket 工廠(SSL/防火牆/連線池)
// 伺服端
LocateRegistry.createRegistry(1099,
new SslRMIClientSocketFactory(),
new SslRMIServerSocketFactory());
// 客戶端
Registry registry = LocateRegistry.getRegistry("host", 1099,
new SslRMIClientSocketFactory());
RMI vs 現代替代方案
| 維度 | RMI | gRPC | Spring Cloud OpenFeign | Dubbo |
| 協定 | JRMP (Java 私有) | HTTP/2 + Protobuf | HTTP/JSON | TCP + Hessian/Kryo |
| 跨語言 | 不支援 | 支援 | 支援 | 支援 |
| 服務發現 | 手動/Registry | 需外掛 | Eureka/Consul/Nacos | 內建註冊中心 |
| 負載均衡 | 無 | Client-side | Ribbon/Spring Cloud LB | 內建策略 |
| 適用場景 | 純 Java 內部系統 | 微服務、多語言 | Spring 生態微服務 | 高效能 RPC、治理 |
常見問題排查
ClassNotFoundException:codebase 路徑錯誤或類未部署
ConnectException: Connection refused:Registry 未啟動、埠號錯誤、防火牆攔截
UnmarshalException:序列化版本不一致(serialVersionUID)、類定義不同
AccessException:SecurityManager 禁止綁定/查找,檢查 policy 檔案
RMI 作為 Java 分散式計算的基石,雖在微服務時代逐漸被跨語言 RPC 取代,但其「物件透明分散」的設計哲學、序列化機制、安全模型仍值得深入學習,為理解現代分散式框架奠定堅實基礎。
- 上一篇文章: J2EE 平台開發 CRM 系統:架構設計、技術治理與組件化實戰指南
- 下一篇文章: Java Vector 應用實戰:動態陣列操作、參數傳遞與 Applet 畫布整合範例
Copyright ©
程式碼之家 All Rights Reserved