1. RMI概念
RMI(Remote Method Invocation),遠程方法調用,是Java的一組擁護開發分佈式應用程序的API。RMI使用Java語言接口定義了遠程對象,它集合了Java序列化和Java遠程方法協議(Java Remote Method Protocol)。簡單地說,這樣使原先的程序在同一操作系統的方法調用,變成了不同操作系統之間程序的方法調用,由於J2EE是分佈式程序平臺,它以RMI機制實現程序組件在不同操作系統之間的通信。比如,一個EJB可以通過RMI調用Web上另一臺機器上的EJB遠程方法。
2. 工作原理
在RMI中,調用遠程對象的對象被稱爲客戶機對象(Client Object)而遠程對象被稱爲服務器對象(Server Object),同時引入了兩種特殊類型對象,存根(stub)和框架(Skelton)。存根實際上是遠程對象的客戶端代理,它和遠程對象具有相同的接口或方法列表,當客戶端調用遠程對象時,實際上是由相應的存根對象代理完成。在服務器端,框架對象處理“遠方”的所有細節,完全可以像編寫本地對象一樣來編寫遠程對象。框架將遠程對象從RMI基礎結構分離開來。也就是說,客戶端獲得的只是代理對象,並不是服務器上的類型,只不過它實現了服務器上類型的全部功能。
3. 實例
實現簡單的查單詞功能,一臺應用服務器以RMI的方式向客戶端提供英譯漢詞典的服務。
創建一個簡單的Java分佈式遠程方法調用程序可以按以下幾個步驟操作:
/**
* 功能說明:定義一個遠程接口,必須繼承Remote接口,其中需要遠程調用的方法必須拋出RemoteException異常
* 作者: gangwazi
* 創建時間:2012-6-27
*/
public interface WordTranslate extends Remote {
/**
* @param str 需要被翻譯的單詞
* @return 英漢互譯後的內容,如果詞典中不包含此單詞返回null
*/
public String translate(String str) throws RemoteException;
}
2)實現遠程接口
/**
* 功能說明:遠程接口的實現
* 作者: gangwazi
* 創建時間:2012-6-27
*/
public class WordTranslateImpl extends UnicastRemoteObject implements WordTranslate {
private static final long serialVersionUID = 1L;
public Map<String, String> wordMap = new HashMap<String, String>();
// 因爲UnicastRemoteObject的構造方法拋出了RemoteException異常,因此這裏默認的構造方法必須寫,必須聲明拋出RemoteException異常
public WordTranslateImpl() throws RemoteException {
super();
wordMap.put("China", "n. 中國");
wordMap.put("Japan", "n. 日本");
wordMap.put("German", "德國");
wordMap.put("list", "n. 列表; v. 列出");
wordMap.put("egg", "n. 雞蛋");
wordMap.put("map", "n. 地圖");
wordMap.put("translate", "v. 翻譯");
wordMap.put("banana", "n. 香蕉");
wordMap.put("apple", "n. 蘋果");
wordMap.put("orange", "n. 橘子");
wordMap.put("milk", "n. 牛奶");
wordMap.put("water", "n. 水");
wordMap.put("drink", "v. 喝,飲");
}
@Override
public String translate(String str) throws RemoteException {
if (wordMap.containsKey(str)) {
return wordMap.get(str);
} else {
return null;
}
}
}
由於只是簡單的示例,故詞典使用map來存儲,當然也可以構造專門的詞典文件或者從數據庫中查詢。3)編寫服務器類
/**
* 功能說明:創建RMI註冊表,啓動RMI服務,並將遠程對象註冊到RMI註冊表中。
* 作者: gangwazi
* 創建時間:2012-6-27
*/
public class WordTranslateServer {
public static void main(String[] args) {
try {
// 創建一個遠程對象
WordTranslate rTranslate = new WordTranslateImpl();
// 本地主機上的遠程對象註冊表Registry的實例,並指定端口爲5555,這一步必不可少(Java默認端口是1099),
// 必不可缺的一步,缺少註冊表創建,則無法綁定對象到遠程註冊表上
LocateRegistry.createRegistry(5555);
// 把遠程對象註冊到RMI註冊服務器上,並命名爲RTranslate
// 綁定的URL標準格式爲:rmi://host:port/name(其中協議名可以省略)
Naming.bind("rmi://localhost:5555/RTranslate", rTranslate);
System.out.println(">>>>>INFO: 遠程WorldTranslate對象綁定成功!");
} catch (RemoteException e) {
System.out.println("創建遠程對象發生異常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("發生URL畸形異常!");
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.out.println("發生重複綁定對象異常!");
e.printStackTrace();
}
}
}
4)編寫客戶端類
/**
* 功能說明:客戶端測試,在客戶端調用遠程對象上的遠程方法,並返回結果。
* 作者: gangwazi
* 創建時間:2012-6-27
*/
public class WorldTranslateClient {
public static void main(String[] args) {
try {
// 在RMI服務註冊表中查找名稱爲RTranslate的對象,並調用其上的方法
WordTranslate rTranslate = (WordTranslate) Naming.lookup("rmi://202.117.10.64:5555/RTranslate");
System.out.print("查詢單詞 China----------->");
System.out.println(rTranslate.translate("China"));
System.out.print("查詢單詞 list----------->");
System.out.println(rTranslate.translate("list"));
System.out.print("查詢單詞 present----------->");
System.out.println(rTranslate.translate("present"));
System.out.print("查詢單詞 banana----------->");
System.out.println(rTranslate.translate("banana"));
System.out.print("查詢單詞 util----------->");
System.out.println(rTranslate.translate("util"));
System.out.print("查詢單詞 drink----------->");
System.out.println(rTranslate.translate("drink"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4. 運行結果
服務器端和客戶端分別運行在兩臺機子上
服務器端運行結果
客戶端運行結果