“嗨!還有一個更開心的話題:RMI。RMI代表遠程方法調用。換句話說,RMI是一種機制,允許來自一台Java機器的對象調用來自另一台Java機器的對象的方法,即使它們在不同的計算機,在不同的國家,或在地球的不同角落。”

“哇!聽起來棒極了。”
“是的。但我只會試著給你一個概述。有了這個,如果你挖得太深,你可能會對它如何工作的細微差別感到困惑。”
“但如果不走極端,那麼 RMI 不僅非常簡單,而且極大地簡化了程序員的生活。為此,我們向它致以最深切的敬意。”
“所以,我們希望 Java 程序中的一個對象調用另一個 Java 程序中的對象的方法。不管這些程序在哪裡運行。”
“讓我們考慮一個最簡單的例子:當兩個程序在同一台計算機上運行時。 要讓程序通過互聯網進行交互,您需要配置 JVM 的權限,但我們今天不做介紹。”
“在Java中,你只能遠程調用接口的方法,不能調用類。”
“那麼,我們有兩個程序,它們如何互相調用對方的方法呢?”
“讓我們考慮這樣一種情況,其中一個程序包含一個對象,而第二個程序想要調用該對象的方法。我們將第一個程序稱為服務器,將第二個程序稱為客戶端。 ”
“首先,我將提供一些示例代碼,然後我們將對其進行分析。”
“那麼我們的程序會做什麼呢?”
“嗯。好吧,為了簡單起見,程序將有一個方法來反轉傳遞給它的字符串。”
“很簡單。”
“好,那我們開始吧:”
“首先,我們需要一個能滿足我們要求的接口:”
interface Reverse extends Remote
{
public String reverse(String str) throws RemoteException;
}
“我創建了一個 Reverse 接口並向其添加了一個 Remote 標記接口,以及一個 RemoteException。調用該方法時可能會發生意外錯誤。如果有任何錯誤,則會拋出此異常。”
“現在我們需要編寫一個實現這個接口的服務器類:”
class ReverseImpl implements Reverse
{
public String reverse(String str) throws RemoteException
{
return new StringBuffer(str).reverse().toString();
}
}
“我明白了。我們用這個方法反轉字符串。”
“是的。”
“現在我們需要讓這個對象可以從另一個程序調用。這是你如何做到的:”
public static final String UNIC_BINDING_NAME = "server.reverse";
public static void main(String[] args) throws Exception
{
// Create an object to be accessible remotely.
final ReverseImpl service = new ReverseImpl();
// Create a registry of shared objects.
final Registry registry = LocateRegistry.createRegistry(2099);
// Create a stub for receiving remote calls.
Remote stub = UnicastRemoteObject.exportObject(service, 0);
// Register the stub in the registry.
registry.bind(UNIC_BINDING_NAME, stub);
// Put the main thread to sleep, or else the program will exit.
Thread.sleep(Integer.MAX_VALUE);
}
“我會逐行解釋的。”
"在第 1 行中,我們在UNIC_BINDING_NAME變量 中為我們的遠程對象(可遠程訪問的對象)存儲一個唯一名稱(我們編寫的) 。如果程序使多個對象可訪問,則每個對像都必須有自己的唯一名稱。我們的對象的唯一名稱是‘server.reverse’。”
“在第 6 行,我們創建了一個可遠程訪問的ReverseImpl對象。將調用其方法。”
"在第 9 行,我們創建了一個稱為註冊表的特殊對象。我們需要使用它來註冊我們共享的對象。JVM 稍後將與它們交互。2099 是一個端口(另一個程序可以用來訪問我們的唯一編號)對象註冊表)。”
“換句話說,要訪問一個對象,你需要知道對象註冊表的唯一編號(端口)和對象的唯一名稱,並且與遠程對象實現的接口具有相同的接口。”
“我明白了。比如:打電話(需要一個號碼)並詢問比爾(一個物體的名稱)?”
“嗯。現在,我們繼續吧。”
"在第 11 行 ,我們創建了一個存根。存根是一個特殊的對象,它接收有關遠程調用的信息,將其解包,反序列化方法參數,並調用所需的方法。然後它序列化結果或異常,如果有的話,並將其全部發回給調用者。”
“我明白了。幾乎。你說它‘反序列化方法參數’。那麼,這意味著遠程方法的參數必須是可序列化的?”
“是的。否則你會如何通過網絡發送它們?是的,有例外,即通過引用傳遞的對象,但我們今天不會談論它們。”
“我們會這樣說:你不能傳遞不可序列化的對象,但如果你真的想傳遞,那麼你可以。但這很痛苦,你知道的。”
“好的。”
“那我們繼續吧。”
“在第 13 行,我們在註冊表中以唯一名稱註冊對象的存根。”
“在第 16 行,我們讓主線程休眠。所有的遠程調用都在單獨的線程上處理。重要的是程序正在運行。所以我們在這裡簡單地讓主線程休眠。就是這樣。”
“好的。”
“太好了,那麼這是一個客戶端的例子:”
public static final String UNIC_BINDING_NAME = "server.reverse";
public static void main(String[] args) throws Exception
{
// Create a registry of shared objects
final Registry registry = LocateRegistry.createRegistry(2099);
// Get the object (actually, this is a proxy object)
Reverse service = (Reverse) registry.lookup(UNIC_BINDING_NAME);
// Call the remote method
String result = service.reverse("Home sweet home.");
}
“我將逐行解釋這段代碼:”
“第 1 行 是遠程對象的唯一名稱。這在客戶端和服務器上必須相同。”
“在第 6 行 ,我們創建了一個 «遠程對象註冊表»。它的端口 (2099) 必須與服務器應用程序的註冊表端口相同。”
"在第 9 行,我們從註冊表中獲取對象。返回的對像是代理對象,並轉換為接口。該接口必須繼承 Remote 標記接口。"
“在第 12 行,我們調用接口的方法,就好像對像是在同一個程序中創建的一樣。沒有區別。”
“太棒了!現在我可以編寫分佈式應用程序了。或者像 Battleship for Android 這樣的遊戲。”
“別這樣,阿米戈!Android 操作系統在第三次嘗試接管世界後於 27 世紀被禁止。機器人無法訪問它。沒有任何方法可以讓你遠離它. 你會開始跑來跑去大喊,«殺死所有人類!»”
“嗯。好吧。但我還是得問迭戈。你永遠不知道,也許他會有一些有趣的事情要說。”
“那你去問問他吧,好吧,明天再說吧。”
“再見,Rishi。感謝有趣的課程。”
GO TO FULL VERSION