Java RMI 框架原理(遠程方法調用)

RMI(即Remote Method Invoke 遠程方法調用)。在Java中,只要一個類extendsjava.rmi.Remote接口,即可成爲存在於服務器端的遠程對象,供客戶端訪問並提供一定的服務。JavaDoc描述:Remote 接口用於標識其方法可以從非本地虛擬機上調用的接口。任何遠程對象都必須直接或間接實現此接口。只有在遠程接口(擴展 java.rmi.Remote 的接口)中指定的這些方法纔可遠程使用 
注意:extendsRemote接口的類或者其他接口中的方法若是聲明拋出了RemoteException異常,則表明該方法可被客戶端遠程訪問調用。 
同時,遠程對象必須實現java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問獲得遠程對象時,該遠程對象將會把自身的一個拷貝以Socket的形式傳輸給客戶端,此時客戶端所獲得的這個拷貝稱爲“存根”,而服務器端本身已存在的遠程對象則稱之爲“骨架”。其實此時的存根是客戶端的一個代理,用於與服務器端的通信,而骨架也可認爲是服務器端的一個代理,用於接收客戶端的請求之後調用遠程方法來響應客戶端的請求。 
RMI 框架的基本原理大概如下圖,應用了代理模式來封裝了本地存根與真實的遠程對象進行通信的細節。
下面給出一個簡單的RMI 應用,其中類圖如下:其中IService接口用於聲明服務器端必須提供的服務(即service()方法),ServiceImpl類是具體的服務實現類,而Server類是最終負責註冊服務器遠程對象,以便在服務器端存在骨架代理對象來對客戶端的請求提供處理和響應。
各個類的源代碼如下:
IService接口:
 
import java.rmi.Remote; 
import java.rmi.RemoteException; 
public interface IService extends Remote { 
  //聲明服務器端必須提供的服務 
  String service(String content) throws RemoteException; 
}
ServiceImpl實現類:
import java.rmi.RemoteException; 
//UnicastRemoteObject用於導出的遠程對象和獲得與該遠程對象通信的存根。 
import java.rmi.server.UnicastRemoteObject; 

public class ServiceImpl extends UnicastRemoteObject implements IService { 

  private String name; 

  public ServiceImpl(String name) throws RemoteException { 
    this.name = name; 
  } 
  @Override 
  public String service(String content) { 
    return "server >> " + content; 
  } 
}
Server類:
/* 
* Context接口表示一個命名上下文,它由一組名稱到對象的綁定組成。 
* 它包含檢查和更新這些綁定的一些方法。 
*/
 
import javax.naming.Context; 
/* 
* InitialContext類是執行命名操作的初始上下文。    
* 該初始上下文實現 Context 接口並提供解析名稱的起始點。 
*/
 
import javax.naming.InitialContext; 
public class Server { 
  public static void main(String[] args) { 
    try { 
      //實例化實現了IService接口的遠程服務ServiceImpl對象 
      IService service02 = new ServiceImpl("service02"); 
      //初始化命名空間 
      Context namingContext = new InitialContext(); 
      //將名稱綁定到對象,即向命名空間註冊已經實例化的遠程服務對象 
      namingContext.rebind("rmi://localhost/service02", service02); 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
    System.out.println("服務器向命名錶註冊了1個遠程服務對象!"); 
  } 
}
Client類:
import javax.naming.Context; 
import javax.naming.InitialContext; 

public class Client { 
  public static void main(String[] args) { 
    String url = "rmi://localhost/"; 
    try { 
      Context namingContext = new InitialContext(); 
      // 檢索指定的對象。 即找到服務器端相對應的服務對象存根 
      IService service02 = (IService) namingContext.lookup(url 
          + "service02"); 
      Class stubClass = service02.getClass(); 
      System.out.println(service02 + " 是 " + stubClass.getName() 
          + " 的實例!"); 
      // 獲得本底存根已實現的接口類型 
      Class[] interfaces = stubClass.getInterfaces(); 
      for (Class c : interfaces) { 
        System.out.println("存根類實現了 " + c.getName() + " 接口!"); 
      } 
      System.out.println(service02.service("你好!")); 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
  } 
}
將以上代碼保存於某一目錄下,先運行“start rmiregistry”來啓動JDK自帶的註冊表程序,它用於保存Server類註冊的遠程對象並允許遠程客戶端的請求訪問;然後運行服務器端的Server類,即“start java Server”,該程序向註冊表中註冊具體的遠程對象;最後纔是運行客戶端程序來查找並獲得服務器端的遠程對象存根,此時才能使用存根對象與服務器進行通信,命令是“java Client”。注意:上面命令中的start的功能是重新打開一個DOS窗口。
運行結果如下:
 
其實整個簡單的RMI 應用中各個類的交互時序如下圖:


發佈了37 篇原創文章 · 獲贊 32 · 訪問量 49萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章