當前位置:才華齋>計算機>java語言>

java遠端方法呼叫技巧

java語言 閱讀(2.62W)

聰明出於勤奮,天才在於積累,以下是小編為大家搜尋整理java遠端方法呼叫技巧,希望能給大家帶來幫助!更多精彩內容請及時關注我們應屆畢業生考試網!

java遠端方法呼叫技巧

  概述

Java Remote Method Invocation ( RMI -- Java遠端方法呼叫)允許您使用Java編寫分散式物件。本文將介紹RMI的優點以及如何將其連線到現有的和原有的系統中,以及與用Java 編寫的元件的連線。

RMI為採用Java物件的分散式計算提供了簡單而直接的途徑。這些物件可以是新的Java物件,也可以是圍繞現有API的簡單的Java包裝程式。Java體現了“編寫一次就能在任何地方執行的模式。而RMI可將Java模式進行擴充套件,使之可在任何地方執行”。

因為RMI是以Java為核心的,所以,它將Java的安全性和可移植性等強大功能帶給了分散式計算。您可將代理和梢務邏輯等屬性移動到網路中最合適的地方。如果您要擴充套件Java在系統中的使用,RMI將使您充分利用其強大功能。

RMI可利用標準Java本機方法介面JNI與現有的和原有的系統相連線。RMI還可利用標準JDBC包與現有的關係資料庫連線。RMI/JNI和RMI/JDBC相結合,可幫助您利用RMI與目前使用非Java語言的現有伺服器進行通訊,而且在您需要時可擴充套件Java在這些伺服器上的使用。RMI可幫助您在擴充套件使用時充分利用Java的強大功能。

  優點

從最基本的角度看,RMI是Java的遠端過程呼叫(RPC)機制。與傳統的RPC系統相比,RMI具有若干優點,因為它是Java面向物件方法的一部分。傳統的RPC系統採用中性語言,所以是最普通的系統--它們不能提供所有可能的目標平臺所具有的功能。

RMI以Java為核心,可與採用本機方法與現有系統相連線。這就是說,RMI可採用自然、直接和功能全面的方式為您提供分散式計算技術,而這種技術可幫助您以不斷遞增和無縫的方式為整個系統新增Java功能。

  RMI的主要優點如下:

面向物件:RMI可將完整的物件作為引數和返回值進行傳遞,而不僅僅是預定義的資料型別。也就是說,您可以將類似Java雜湊表這樣的複雜型別作為一個引數進行傳遞。而在目前的RPC系統中,您只能依靠客戶機將此類物件分解成基本資料型別,然後傳遞這些資料型別,最後在伺服器端重新建立雜湊表。RMI則不需額外的客戶程式程式碼(將物件分解成基本資料型別),直接跨網傳遞物件。

可移動屬性:RMI可將屬性(類實現程式)從客戶機移動到伺服器,或者從伺服器移到客戶機。例如,您可以定義一個檢查僱員開支報告的介面,以便察看僱員是否遵守了公司目前實行的政策。在開支報告建立後,客戶機就會從伺服器端獲得實現該介面的物件。如果政策發生變化,伺服器端就會開始返回使用了新政策的該介面的另一個實現程式。您不必在使用者系統上安裝任何新的軟體就能在客戶端檢查限制條件--從而向用戶提供爍快的反饋,並降低伺服器的工作量。這樣就能具備最大的靈活性,因為政策改變時只需要您編寫一個新的Java類,並將其在伺服器主機上安裝一次即可。

設計方式:物件傳遞功能使您可以在分散式計算中充分利用面向物件技術的強大功能,如二層和三層結構系統。如果您能夠傳遞屬性,那麼您就可以在您的解決方案中使用面向物件的設計方式。所有面向物件的設計方式無不依靠不同的屬性來發揮功能,如果不能傳遞完整的物件--包括實現和型別--就會失去設計方式上所提供的優點。

安全:RMI使用Java內建的安全機制保證下載執行程式時使用者系統的安全。RMI使用專門為保護系統免遭惡意小應用程式侵害而設計的安全管理程式,可保護您的系統和網路免遭潛在的惡意下載程式的破壞。在情況嚴重時,伺服器可拒絕下載任何執行程式。

便於編寫和使用:RMI使得Java遠端服務程式和訪問這些服務程式的Java客戶程式的編寫工作變得輕鬆、簡單。遠端介面實際上就是Java介面。服務程式大約用三行指令宣佈本身是服務程式,其它方面則與任何其它Java物件類似。這種簡單方法便於快速編寫完整的分散式物件系統的服務程式,並快速地製做軟體的原型和早期版本,以便於進行測試和評估。因為RMI程式編寫簡單,所以維護也簡單。

可連線現有/原有的系統:RMI可通過Java的本機方法介面JNI與現有系統進行進行互動。利用RMI和JNI,您就能用Java語言編寫客戶端程式,還能使用現有的伺服器端程式。在使用RMI/JNI與現有伺服器連線時,您可以有選擇地用Java重新編寫服務程式的任何部分,並使新的程式充分發揮Java的功能。類似地,RMI可利用JDBC、在不修改使用資料庫的現有非Java原始碼的前提下與現有關係資料庫進行互動。

編寫一次,到處執行:RMI是Java“編寫一次,到處執行 ”方法的一部分。任何基於RMI的系統均可100%地移植到任何Java虛擬機器上,RMI/JDBC系統也不例外。如果使用RMI/JNI與現有系統進行互動工作,則採用JNI編寫的程式碼可與任何Java虛擬機器進行編譯、執行。

分散式垃圾收集:RMI採用其分散式垃圾收集功能收集不再被網路中任何客戶程式所引用的遠端服務物件。與Java 虛擬機器內部的垃圾收集類似,分散式垃圾收集功能允許使用者根據自己的需要定義伺服器物件,並且明確這些物件在不再被客戶機引用時會被刪除。

平行計算:RMI採用多執行緒處理方法,可使您的伺服器利用這些Java執行緒更好地並行處理客戶端的請求。Java分散式計算解決方案:RMI從JDK 1.1開始就是Java平臺的核心部分,因此,它存在於任何一臺1.1 Java虛擬機器中。所有RMI系統均採用相同的公開協議,所以,所有Java 系統均可直接相互對話,而不必事先對協議進行轉換。

  傳遞屬性

前面我們講到,RMI可以傳遞屬性,並簡單介紹了一下一個有關開支報告程式的情況。下面我們將深入討論如何設計這樣的系統。這樣介紹的目的是使您能夠利用RMI的功能將屬性從一個系統傳遞到另一個系統,並隨心所欲地安排當前的計算地點,並便於將來的改變。下面的例子並未涉及真實世界可能發生的所有問題,但可幫助讀者瞭解處理問題的方法。

  伺服器定義的策略

圖1是可進行動態配置的開支報告系統的示意圖。客戶機向用戶顯示圖形使用者介面(GUI),使用者填寫開支報告。客戶機程式使用RMI與伺服器進行通訊。伺服器使用JDBC( Java關係資料庫連線包)將開支報告儲存在資料庫中。至此,這看起來與其它多層次系統大同小異,但有一個重大區別-- RMI能下載屬性。

假定公司關於開支報告的政策發生改變。例如,目前公司只要求對超過20美元的開支需開具發票。但到明天,公司認為這太寬鬆了,便決定除不超過20美元的餐費以外,任何開支均需開具發票。如果不能下載屬性的話,那麼在設計便於修改的系統時您可選擇下列方法之一:

用客戶端安裝與政策有關的程式。政策改變時,必須更新包含此政策的所有客戶端程式。您可在若干伺服器上安裝客戶程式,並要求所有使用者從這些伺服器之一執行客戶程式,從而減少問題。但這仍不能徹底解決問題-- 那些讓程式執行好幾天的使用者就無法使程式更新,而總是會有一些使用者為了方便而把軟體複製到本地磁碟上。

您可要求伺服器在每次向開支報告新增專案時檢查政策。但這樣就會在客戶機和伺服器之間產生大量資料流,並增加伺服器的工作量。這還會使系統變得更加脆弱--網路故障會立即妨礙使用者,而不僅僅是隻在其呈交開支報告或啟動新的報告時對其產生影響。同時,新增專案的速度也會變慢,因為這需要穿越整個網路往返一圈才能到達(不堪重負的)伺服器。

您可在呈交報告時要求伺服器對政策進行檢查。這樣就會使使用者建立很多必須待批報告的錯誤專案,而不是立刻捕捉到第一個錯誤,從而使使用者有機會停止製造錯誤。為避免浪費時間,使用者需要立刻得到有關錯誤的反饋。

有了RMI,您就能以簡單的方法呼叫程式從伺服器得到屬性,從而提供了一種靈活的方式,將計算任務從伺服器解除安裝到客戶機上,同時為使用者提供速度更快的反饋。當用戶準備編寫一份新的開支報告時,客戶機就會向伺服器要求一個物件,該物件嵌入了適用於該開支報告的當前政策,就如同通過用Java編寫的政策介面所表示的那樣。該物件可以以任何方式實現當前政策。如果這是客戶機RMI首次看到這種專門執行的政策,就會要求伺服器提供一份執行過程的副本。如果執行過程將來發生變化,則一種新的政策物件將被返回給客戶機,而RMI執行時則會要求得到新的執行過程。

這表明,政策永遠是動態的。您要想修改政策,就只需簡單地編寫通用政策介面的新的執行程式,把它安裝在伺服器上,並對伺服器進行配置以返回這種新型別的物件即可。這樣,每臺客戶機都會根據新的政策對新的開支報告進行檢查。

這是一種比任何靜態方法都更好的方法,原因如下:

所有客戶機不必暫停或用新的軟體來升級--軟體可根據需要在不工作時加以更新。

伺服器不必參與專案檢查工作,該工作可在本地完成。

允許動態限制,因為物件執行程式(而不僅僅是資料)是在客戶機和伺服器之間進行傳遞的。

使使用者能立刻看到錯誤。

使客戶機在伺服器上所能呼叫的方法的遠端介面定義如下:

import .*;

public interface ExpenseServer extends Remote {

Policy getPolicy() throws RemoteException;

void submitReport(ExpenseReport report)

throws RemoteException, InvalidReportException;

}

import語句輸入Java RMI包。所有RMI型別均在包或其子包內定義。介面ExpenseServer是一般的Java介面,具有如下兩種有趣的特點:

它擴充套件了名為Remote的RMI介面,這使該介面標記為可用於遠端呼叫。

它的所有方法均丟擲RemoteException,後者用來表示網路或資訊故障。

遠端方法還可丟擲您所需要的任何其他例外,但至少必須丟擲RemoteException,這樣您才能處理只會在分散式系統中發生的錯誤狀態。該介面本身支援兩個方法:getPolicy (返回一個實現政策介面的物件),和submitReport (提交一個完成的開支請求,並在報告無論因何種原因使表格出現錯誤時,丟擲一個例外)。

政策介面本身可宣告一種使客戶機知道能否在開支報告中新增一個專案的方法:

public interface Policy {

void checkValid (Expenseentry entry)

throws PolicyViolationException;

}

如果該專案有效--即符合當前政策,則該方法可正常返回。否則就會丟擲一個描述該錯誤的例外。政策介面是本地的(而非遠端的),所以將通過本機物件在客戶機上執行--在客戶機的虛擬機器上,而非整個網路上執行的物件。客戶機可能執行下列程式:

Policy curPolicy = olicy();

start a new expense report

show the GUI to the user

while (user keeps adding entries) {

try {

kValid(entry); // throws exception if not OK

add the entry to the expense report

} catch (PolicyViolationException e) {

show the error to the user

}

}

itReport(report);

當用戶請求客戶機軟體啟動一份新的開支報告時,客戶機就呼叫olicy,並要求伺服器返回一個包含當前開支政策的物件。新增的每個專案首先都被提交給該政策物件,以獲得批准。如果政策物件報告無錯誤,則該專案就被新增到報告中;否則錯誤就被顯示給使用者,而後者可採取修正措施。當用戶完成向報告中新增專案時,整個報告就被呈交。服務程式如下:

import . *;

import er. *;

class ExpenseServerImpl

extends UnicastRemoteObject

implements ExpenseServer

{

ExpenseServerImpl() throws RemoteException {

// . . . set up server state . . .

}

public Policy getPolicy() {

return new TodaysPolicy();

}

public void submitReport(ExpenseReport report) {

// . . . write the report into the db . . .

}

}

除基本程式包外,我們還輸入RMI的服務程式包。型別UnicastRemoteObject 定義了此服務程式遠端物件的型別,在本例中,應為單一服務程式而非複製服務(下面還會詳細介紹)。Java類ExpenseSeverImpl實現遠端接ExpenseServer的方法。遠端主機的客戶機可使用RMI將資訊傳送給ExpenseServerImpl物件。

本文中討論的重要方法是getPolicy,它簡單地返回定義當前政策的物件。下面看一個執行政策的例子:

public class TodaysPolicy implements Policy {

public void checkValid(ExpenseEntry entry)

throws PolicyViolationException

{

if (ars() < 20) {

return; // no receipt required

else if (Receipt() == false) {

throw new PolicyViolationException;

}

}

}

TodaysPolicy進行檢查的目的是確保無收據的任何專案均少於20美元。如果將來政策發生變化,僅少於20美元的餐費可不受“需要收據”政策的限制,則您可提供新的政策實現:

public class TomorrowsPolicy implements Policy {

public void checkValid(ExpenseEntry entry)

throws PolicyViolationException

{

if (al() && ars() < 20) {

return; // no receipt required

} else if (Receipt() == false) {

throw new PolicyViolationException;

}

}

}

編寫這個類,並把它安裝在伺服器上,然後告訴伺服器開始提供TomorrowsPolicy物件,而非daysPolicy物件,這樣您的整個系統就會開始使用新的政策。當客戶機呼叫伺服器的getPolicy方法時,客戶機的RMI就會檢查返回的物件是否為已知型別。每臺客戶機首次遇到TomorrowsPolicy時,RMI就會在getPolicy返回前下載政策的實現。客戶機可輕鬆地開始增強這個新的政策。

RMI使用標準Java物件序列化機制傳遞物件。引用遠端物件引數作為遠端引用傳遞。如果向某方法提供的引數為原始型別或本機(非遠端)物件,則向伺服器傳遞一個深副本。返回值也拾照同樣的方式處理,只不過是沿其它方向。RMI可使使用者向本機物件傳遞和返回完整物件圖併為遠端物件傳遞和返回引用。

在真實的系統中,getPolicy方法可能會有一個可以識別使用者及開支報告型別(差旅、客戶關係等)的引數,這樣可使政策加以區別。您或者可以不要求單獨的政策和開支報告物件,但您可以有一種newExpenseReport方法,它可返回一個直接檢查政策的ExpenseReport物件。這最後一種策略可使您像修改政策一樣簡單地修改開支報告的內容--當公司決定需要把餐費劃分為早餐、午餐和晚餐專案,而且像上述修改政策一樣簡單地執行修改時--可編寫一個實現該報告的新類,客戶程式就會自動使用這個類。

  計算伺服器

開支報告的例子表示了客戶機如何從伺服器得到屬性。屬性可沿兩個方向傳遞--客戶機也可將新的型別傳遞給使用者。最簡單的例子就是如圖2所示的計算伺服器,該服務程式可執行任意任務,這樣整個企業內的客戶機都能利用高階或專用計算機

任務由一個簡單的本地(非遠端)介面定義:

public interface Task {

Object run();

}

執行時,它就會進行一些計算,並返回一個包含結果的物件。這完全是一般性的任務--幾乎所有計算任務都可在這個介面下實現。遠端介面ComputeServer也同樣簡單:

import .*;

public interface ComputeServer extends Remote {

Object compute(Task task) throws RemoteException;

}

這個遠端介面的.唯一目的就是使客戶機建立一個Task (任務)物件,並把它傳送給伺服器執行,最後返回結果。該伺服器的基本實現如下:

import .*;

import er.*;

public class ComputeServerImpl

extends UnicastRemoteObject

implements ComputeServer

{

public ComputeServerImpl() throws RemoteException { }

public Object compute(Task task) {

return ();

}

public static void main(String[] args) throws Exception {

// use the default, restrictive security manager

ecurityManager(new RMISecurityManager());

ComputeServerImpl server = new ComputeServerImpl();

nd("ComputeServer", server);

tln("Ready to receive tasks");

return;

}

}

如果您看一看compute方法就會發現,它非常簡單。它只是接受傳遞給它的物件,並返回計算的結果。main方法包括伺服器的啟動程式碼--它安裝了RMI的預設安全管理程式,以防止他人存取本地系統,並建立可處理進入的請求的ComputeServerImpl物件,並將其與名字"ComputeServer"關聯。這時,伺服器已經準備好接收要執行的任務,而main 也完成了其設定。

如上所述,這實際上是一種全面和實用的服務。系統可以得到改進,比如,可新增要計算的引數,從而對使用服務程式的部門進行計費。但在很多情況下,上述介面和及其實現允許使用高階計算機進行遠端計算。這又明瞭RMI的簡單性--如果您鍵入上述類,對其進行編譯,並啟動服務程式,您就擁有了能執行任意任務的執行計算伺服器。

下面介紹一個使用這種計算服務的例子。假定您購買了一個能執行大量計算操作應用程式的非常高階的系統。管理員可在該系統上啟動一個Java虛擬機器,執行ComputeServerImpl物件。該物件現在就可接受要執行的任務。

現在假定一個小組準備通過一組資料培訓一個神經網路,以幫助制訂採購策略。他們可以採用的步驟如下:

定義一個類--暫且稱之為PurchaseNet。它能接受一組資料,並執行培訓資料,返回一個經過培訓的神經網路。PurchaseNet 將實現Task (任務)介面,並在其run方法中執行其工作。他們可能還需要一個Neuron類來描述所返回的網路中的節點,而且很可能需要其它類來描述處理過程。run方法將返回一個由一組經過培訓的Neuron物件組成的NeuralNet物件。

當這些類被編寫好並進行小規模測試時,用一個PurchaseNet 物件呼叫ComputeServer的compute方法。

當ComputeServerImpl物件中的RMI系統接收到作為進入引數的 PurchaseNet物件時,它就下載PurchaseNet的實現,並呼叫該伺服器的compute方法,並把該物件作為Task (任務)引數。

Task,也就是PurchaseNet物件,將開始執行其執行程式。當執行程式需要諸如Neuron和NeuralNet等新的類時,它們可根據需要下載。

所有計算都將在計算伺服器上執行,而客戶機執行緒則等待結果。(客戶機系統上的另一個執行緒則會顯示“正在等待”游標或使用Java的內建並行機制執行另一個任務 )。當執行返回其NeuralNet物件時,這個物件將作為compute 方法的結果被傳遞迴客戶機。

這不需要在伺服器上安裝其它軟體--所有必須完成的任務都由各部門在其自己的計算機上完成,隨後再根據需要傳遞給計算伺服器主機。

這個簡單的計算伺服器體系結構為系統的分散式功能提供了功能強大的轉換能力。一項任務的計算可以被轉移到一個能為其提供最好支援的系統上完成。這樣的系統可以被用來:

在ComputeServerImpl物件運行於有資料探勘需要的主機上,支援資料探勘應用程式。這樣可使您輕鬆地把任何計算移動到資料所在的地方。

在從當前股票價格、發貨資訊或其它實時資訊等外部資源獲得直接資料的伺服器上執行計算任務。

通過執行ComputeServer (接受進入的請求並將其轉送到執行 ComputeServerImpl的負擔最小的伺服器上)的不同實現,而將任務分佈在多個伺服器上。

代理

因為RMI允許使用Java實現下載屬性,所以您可使用RMI 編寫代理系統。代理的最簡單格式如下:

import .*;

public interface AgentServer extends Remote {

void accept(Agent agent)

throws RemoteException, InvalidAgentException;

}

public interface Agent extends alizable {

void run();

}

啟動一個代理也就建立了實現Agent (代理)介面、找到伺服器、啟用接受該代理物件的類。該代理的執行程式將被下載到伺服器上執行。accept方法將啟動一個該代理的新執行緒,啟用其run方法,然後返回,從而使該代理一直執行到run方法返回為止。代理可通過啟用在另一臺主機上執行的服務程式的accept方法而移植到該主機,而其本身則作為將被接受的代理來傳遞,並結束其在原來主機上的執行緒。

面向物件的程式碼重用與設計模式

面向物件的程式設計是一項允許程式碼重用的強大技術。很多企業組織都使用面向物件的程式設計來減輕建立程式的負擔和提高系統的靈活性。RMI是完全面向物件的--資訊被髮送給遠端物件,而且物件可以被傳遞和返回。

Design Patterns (設計模式)目前在描述面向物件設計的實踐活動中獲得了相當大的成功。首先是因為Design Patterns 的創新工作而使之大受歡迎,這些程式設計方式是一種正式描述解決某類特定問題的完整方法的途徑。所有這些設計模式都依賴於建立一個或多個抽象概念,這些抽象概念允許不同的實現,從而允許和增強軟體重用。軟體重用是面向物件技術的主要優勢,而設計模式則是促進重用的最受歡迎的技術之一。

所有設計模式都依賴面向物件的多型性--這是物件( 如Task )擁有多個實現的能力。演算法的普通部分(如compute 方法)不必知道使用了哪個實現,它只需知道得到一個物件後應該對該物件採取什麼操作。特別地,計算伺服器就是Command (指令)模式的一個例子,它可使您將請求 (任務)表示為物件,並對其進行排程。

只有當包括執行程式在內的完整物件能在客戶機和伺服器之間傳遞時,才會存在這樣的多型性。DCE和DCOM等傳統的RPC系統以及CORBA等基於物件的RPC系統不能下載並執行程式,因為它們不能把真實物件作為引數傳遞,而只能傳遞資料。

RMI可傳遞包括執行程式在內的所有型別,所以您可以在分散式計算解決方案中,而不僅僅是本地計算中使用面向物件的程式設計--包括設計方式。如果沒有RMI這樣完全面向物件的系統,那麼您就必須放棄很多分散式計算系統中的設計方式--以及面向物件的軟體重用的其它形式。