最近在看《Head First設計模式》一書的代理模式時,提到了Java RMI,即Java遠程方法調用這個概念。於是上網找了一些關於Java RMI的資料加以學習。
一、Java RMI概觀
RMI英文全稱為:Remote Method Invocation,中文意思為:遠程方法調用。它是一種機制,能夠讓在某個 JAVA 虛擬機器上的對象調用另一個 JAVA 虛擬機器中的對象上的方法。可以用此方法調用的任何對象必須實現該遠程介面。
RMI提供了客戶輔助對象和服務輔助對象,為客戶輔助對象建立和服務物件相同的方法。RMI的好處在於你不必親自寫如何網路代碼和I/O代碼。客戶程式遠程調用(即真正的服務所在)就和運行客戶自己的本地機JVM上對對象進行正常方法調用一樣。
RMI將客戶輔助對象成為stub(樁),服務輔助對象成為skeleton(骨架)。
RMI結構圖如下:
二、Java中如何利用RMI進行遠程方法調用呢?
步驟如下:
1、製作遠程介面
2、製作遠端實現
3、利用rmic產生stub和skeleton
4、啟動RMI registry(rmiregistry)
5、開始遠程服務
三、例子程式
MyRemote.java
/** * 伺服器端 遠程介面 */package RMIDemo;/* * RemoteException和遠程介面在java.rmi包中 * */import java.rmi.*;/* * 定義一個遠程介面,它必須擴充自java.rmi.Remote介面。 * */public interface MyRemote extends Remote {/** * 所有的遠程方法都必須拋出RemoteException異常。 * @return * @throws RemoteException */public String sayHello() throws RemoteException;}
MyRemoteImpl.java
/** * 遠程服務(實現) */package RMIDemo;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;/** * * @author ccf * *//** * 要想建立一個遠程對象,擴充UnicastRemoteObject是最容易的方法。 * 其中,MyRemote介面是你必須實現的遠程介面 */public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {/** * 你必須實現遠程介面。當然你必須實現所有的介面方法,但請注意:可以不需要聲明RemoteException。 */@Overridepublic String sayHello() throws RemoteException {// TODO Auto-generated method stubreturn "Server says: 'Hey' ";}/** * 你的超類(UnicastRemoteObject)構造器聲明了異常,所以你必須寫一個構造器, * 因為這意味著你的構造器正在調用不安全的代碼(它的超構造器)。 * @throws RemoteException */public MyRemoteImpl() throws RemoteException{}/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubtry{/** * 先產生遠程對象,再使用Naming.rebind()綁定到rmiregistry。 * 客戶將使用你所註冊的名稱在RMI Registry中尋找它。 */MyRemote service = new MyRemoteImpl();Naming.rebind("RemoteHello", service);}catch(Exception ex){ex.printStackTrace();}}}
MyRemoteClient.java
package RMIDemo;/** * 用來做rmiregister lookup的Naming類在java.rmi包中。 * 用戶端測試,在用戶端調用遠程對象的遠程方法,並返回結果。 */import java.rmi.*;public class MyRemoteClient {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stub}public void go(){try{/* * 傳回值是Object類型,所以別忘記轉換類型。 * 其次,在lookup函數中你需要IP地址或主機名稱,還有伺服器被綁定/重綁定時用的名稱。 */MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello");String s = service.sayHello();System.out.println(s);}catch(Exception ex){ex.printStackTrace();}}}
部落格園博主ninahan的部落格Java
RMI之HelloWorld篇的例子和我從《Head First設計模式》中的摘抄的例子差不多,他講解得比較清晰!
總結:RMI對伺服器的IP地址和連接埠依賴很緊密,但是在開發的時候不知道將來的伺服器IP和連接埠如何,但是用戶端程式依賴這個IP和連接埠。這也是RMI的局限性之一。這個問題有兩種解決途徑:一是通過DNS來解決,二是通過封裝將IP暴露到程式碼之外。RMI的局限性之二是RMI是Java語言的遠程調用,兩端的程式語言必須是Java實現,對於不同語言間的通訊可以考慮用Web Service或者公用對象請求代理體系(CORBA)來實現。
四、 參考資料:
1、《Head First設計模式》第440-450頁。
2、Java RMI 伺服器架構:使用非同步進程管理器來包含 RMI 伺服器應用程式
3、Java RMI之HelloWorld篇:Java RMI之HelloWorld篇
4、JAVA RMI執行緒模式及內部實現機制 :JAVA RMI執行緒模式及內部實現機制
5、JDK_API_1_6_zh_CN.CHM參考手冊