RMI(即Remote Method Invoke 遠程方法調(diào)用)。在Java中,只要一個類extends了java.rmi.Remote接口,即可成為存在于服務(wù)器端的遠程對象,供客戶端訪問并提供一定的服務(wù)。JavaDoc描述:Remote 接口用于標識其方法可以從非本地虛擬機上調(diào)用的接口。任何遠程對象都必須直接或間接實現(xiàn)此接口。只有在“遠程接口”(擴展 java.rmi.Remote 的接口)中指定的這些方法才可遠程使用。
注意:extends了Remote接口的類或者其他接口中的方法若是聲明拋出了RemoteException異常,則表明該方法可被客戶端遠程訪問調(diào)用。
同時,遠程對象必須實現(xiàn)java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問獲得遠程對象時,該遠程對象將會把自身的一個拷貝以Socket的形式傳輸給客戶端,此時客戶端所獲得的這個拷貝稱為“存根”,而服務(wù)器端本身已存在的遠程對象則稱之為“骨架”。其實此時的存根是客戶端的一個代理,用于與服務(wù)器端的通信,而骨架也可認為是服務(wù)器端的一個代理,用于接收客戶端的請求之后調(diào)用遠程方法來響應(yīng)客戶端的請求。
RMI 框架的基本原理大概如下圖,應(yīng)用了代理模式來封裝了本地存根與真實的遠程對象進行通信的細節(jié)。
下面給出一個簡單的RMI 應(yīng)用,其中類圖如下:其中IService接口用于聲明服務(wù)器端必須提供的服務(wù)(即service()方法),ServiceImpl類是具體的服務(wù)實現(xiàn)類,而Server類是最終負責注冊服務(wù)器遠程對象,以便在服務(wù)器端存在骨架代理對象來對客戶端的請求提供處理和響應(yīng)。
各個類的源代碼如下:
IService接口:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IService extends Remote {
//聲明服務(wù)器端必須提供的服務(wù)
String service(String content) throws RemoteException;
}
ServiceImpl實現(xiàn)類:
import java.rmi.RemoteException;
//UnicastRemoteObject用于導(dǎo)出的遠程對象和獲得與該遠程對象通信的存根。
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類是執(zhí)行命名操作的初始上下文。
* 該初始上下文實現(xiàn) Context 接口并提供解析名稱的起始點。
*/
import javax.naming.InitialContext;
public class Server {
public static void main(String[] args) {
try {
//實例化實現(xiàn)了IService接口的遠程服務(wù)ServiceImpl對象
IService service02 = new ServiceImpl("service02");
//初始化命名空間
Context namingContext = new InitialContext();
//將名稱綁定到對象,即向命名空間注冊已經(jīng)實例化的遠程服務(wù)對象
namingContext.rebind("rmi://localhost/service02", service02);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("服務(wù)器向命名表注冊了1個遠程服務(wù)對象!");
}
}
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();
// 檢索指定的對象。 即找到服務(wù)器端相對應(yīng)的服務(wù)對象存根
IService service02 = (IService) namingContext.lookup(url
+ "service02");
Class stubClass = service02.getClass();
System.out.println(service02 + " 是 " + stubClass.getName()
+ " 的實例!");
// 獲得本底存根已實現(xiàn)的接口類型
Class[] interfaces = stubClass.getInterfaces();
for (Class c : interfaces) {
System.out.println("存根類實現(xiàn)了 " + c.getName() + " 接口!");
}
System.out.println(service02.service("你好!"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
將以上代碼保存于某一目錄下,先運行“start rmiregistry”來啟動JDK自帶的注冊表程序,它用于保存Server類注冊的遠程對象并允許遠程客戶端的請求訪問;然后運行服務(wù)器端的Server類,即“start java Server”,該程序向注冊表中注冊具體的遠程對象;最后才是運行客戶端程序來查找并獲得服務(wù)器端的遠程對象存根,此時才能使用存根對象與服務(wù)器進行通信,命令是“java Client”。注意:上面命令中的start的功能是重新打開一個DOS窗口。
運行結(jié)果如下:
其實整個簡單的RMI 應(yīng)用中各個類的交互時序如下圖:
以上內(nèi)容是學(xué)習(xí)參考了孫衛(wèi)琴老師的《Java網(wǎng)絡(luò)編程精解》一書的RMI一章,加上了自己個人的理解總結(jié),希望能與大家互相學(xué)習(xí)共同進步!