日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

RMI 相關(guān)知識(shí)

系統(tǒng) 2334 0

文章轉(zhuǎn)自:http://www.blogjava.net/zhenyu33154/articles/320245.html

?

RMI 全稱是 Remote?Method?Invocation -遠(yuǎn)程方法調(diào)用, Java?RMI JDK1.1 中實(shí)現(xiàn)的,其 威力就體現(xiàn)在它強(qiáng)大的開發(fā)分布式網(wǎng)絡(luò)應(yīng)用的能力上 ,是純 Java 的網(wǎng)絡(luò)分布式應(yīng)用系統(tǒng)的核心解決方案之一。 其實(shí)它可以被看作是 RPC Java 版本。但是傳統(tǒng) RPC 并不能很好地應(yīng)用于分布式對(duì)象系統(tǒng)。而 Java?RMI? 則支持存儲(chǔ)于不同地址空間的程序級(jí)對(duì)象之間彼此進(jìn)行通信,實(shí)現(xiàn)遠(yuǎn)程對(duì)象之間的無縫遠(yuǎn)程調(diào)用。

?

RMI 目前使用 Java 遠(yuǎn)程消息交換協(xié)議 JRMP Java?Remote?Messaging?Protocol )進(jìn)行通信 由于 JRMP 是專為 Java 對(duì)象制定的 Java?RMI 具有 Java "Write?Once,Run?Anywhere" 的優(yōu)點(diǎn),是分布式應(yīng)用系統(tǒng)的百分之百純 Java 解決方案。用 Java?RMI 開發(fā)的應(yīng)用系統(tǒng)可以部署在任何支持 JRE Java?Run?Environment?Java ,運(yùn)行環(huán)境)的平臺(tái)上。但由于 JRMP 是專為 Java 對(duì)象制定的,因此, RMI 對(duì)于用非 Java 語言開發(fā)的應(yīng)用系統(tǒng)的支持不足。不能與用非 Java 語言書寫的對(duì)象進(jìn)行通信。

?

RMI 可利用標(biāo)準(zhǔn) Java 本機(jī)方法接口 JNI 與現(xiàn)有的和原有的系統(tǒng)相連接。 RMI 還可利用標(biāo)準(zhǔn) JDBC 包與現(xiàn)有的關(guān)系 數(shù)據(jù)庫(kù) 連接。 RMI/JNI RMI/JDBC 相結(jié)合,可幫助您利用 RMI 與目前使用非 Java 語言的現(xiàn)有服務(wù)器進(jìn)行通信,而且在您需要時(shí)可擴(kuò)展 Java 在這些服務(wù)器上的使用。 RMI 可幫助您在擴(kuò)展使用時(shí)充分利用 Java 的強(qiáng)大功能。

?

?

?

一、 RMI (遠(yuǎn)程方法調(diào)用)的組成

?

一個(gè)正常工作的 RMI 系統(tǒng)由下面幾個(gè)部分組成:?

?

· 遠(yuǎn)程服務(wù)的接口定義?
· 遠(yuǎn)程服務(wù)接口的具體實(shí)現(xiàn)?
· 樁( Stub )和框架( Skeleton )文件?
· 一個(gè)運(yùn)行遠(yuǎn)程服務(wù)的服務(wù)器?
· 一個(gè) RMI 命名服務(wù),它允許 客戶端 去發(fā)現(xiàn)這個(gè)遠(yuǎn)程服務(wù)?
· 類文件的提供者(一個(gè) HTTP 或者 FTP 服務(wù)器)?
· 一個(gè)需要這個(gè)遠(yuǎn)程服務(wù)的客戶端程序?

?

?

?

二、 RMI (遠(yuǎn)程方法調(diào)用)原理 示意圖

?

RMI 相關(guān)知識(shí)

?


?

方法調(diào)用從客戶對(duì)象經(jīng)占位程序( Stub) 、遠(yuǎn)程引用層 (Remote?Reference?Layer) 和傳輸層( Transport?Layer )向下,傳遞給主機(jī),然后再次經(jīng)傳?輸層,向上穿過遠(yuǎn)程調(diào)用層和骨干網(wǎng)( Skeleton ), 到達(dá)服務(wù)器對(duì)象。?占位程序扮演著遠(yuǎn)程服務(wù)器對(duì)象的代理的角色,使該對(duì)象可被客戶激活。?遠(yuǎn)程引用層處理語義、管理單一或多重對(duì)象的通信,決定調(diào)用是應(yīng)發(fā) 往一個(gè)服務(wù)器還是多個(gè)。傳輸層管理實(shí)際的連接,并且追蹤可以接受方法調(diào)用的遠(yuǎn)程對(duì)象。服務(wù)器端的骨干網(wǎng)完成對(duì)服務(wù)器對(duì)象實(shí)際的方法調(diào)用,并獲取返回值。返 回值向下經(jīng)遠(yuǎn)程引用層、服務(wù)器端的傳輸層傳遞回客戶端,再向上經(jīng)傳輸層和遠(yuǎn)程調(diào)用層返回。最后,占位程序獲得返回值。?

?

要完成以上步驟需要有以下幾個(gè)步驟:?

?

1、? 生成一個(gè)遠(yuǎn)程接口?

?

2、? 實(shí)現(xiàn)遠(yuǎn)程對(duì)象 ( 服務(wù)器端程序 )

?

3、? 生成占位程序和骨干網(wǎng) ( 服務(wù)器端程序 )

?

4、? 編寫服務(wù)器程序?

?

5、? 編寫客戶程序?

?

6、? 注冊(cè)遠(yuǎn)程對(duì)象?

?

7、? 啟動(dòng)遠(yuǎn)程對(duì)象?

?

?

?

三、 RMI (遠(yuǎn)程方法調(diào)用)的優(yōu)點(diǎn)?

?

從最基本的角度看, RMI Java 遠(yuǎn)程過程調(diào)用 (RPC) 機(jī)制。與傳統(tǒng)的 RPC 系統(tǒng)相比, RMI 具有若干優(yōu)點(diǎn),因?yàn)樗? Java 面向?qū)ο蠓椒? 的一部分。傳統(tǒng)的 RPC 系統(tǒng)采用中性語言,所以是最普通的系統(tǒng) -- 它們不能提供所有可能的目標(biāo)平臺(tái)所具有的功能。?

?

RMI Java 為核心,可與采用本機(jī)方法與現(xiàn)有系統(tǒng)相連接。這就是說, RMI 可采用自然、直接和功能全面的方式為您提供分布式計(jì)算技術(shù),而這種技術(shù)可幫助您以不斷遞增和無縫的方式為整個(gè)系統(tǒng)添加 Java 功能。

?

RMI 的主要優(yōu)點(diǎn)如下:?

?

面向?qū)ο? RMI 可將完整的對(duì)象作為參數(shù)和返回值進(jìn)行傳遞,而不僅僅是預(yù)定義的數(shù)據(jù) 類型 。也就是說,您可以將類似 Java 哈希表這樣的復(fù)雜類型作為一個(gè)參數(shù)進(jìn)行傳遞。而在目前的 RPC 系統(tǒng)中,您只能依靠客戶機(jī)將此類對(duì)象分解成基本數(shù)據(jù)類型,然后傳遞這些數(shù)據(jù)類型,最后在服務(wù)器端重新創(chuàng)建哈希表。 RMI 則不需額外的客戶程序代碼 ( 將對(duì)象分解成基本數(shù)據(jù)類型 ) ,直接跨網(wǎng)傳遞對(duì)象。?

?

可移動(dòng)屬性: RMI 可將屬性 ( 類實(shí)現(xiàn)程序 ) 從客戶機(jī)移動(dòng)到服務(wù)器,或者從服務(wù)器移到客戶機(jī)。這樣就能具備最大的靈活性,因?yàn)檎吒淖儠r(shí)只需要您編寫一個(gè)新的 Java 類,并將其在服務(wù)器主機(jī)上安裝一次即可。?

?

設(shè)計(jì)方式: 對(duì)象傳遞功能使您可以在分布式計(jì)算中充分利用面向?qū)ο蠹夹g(shù)的強(qiáng)大功能,如二層和三層結(jié)構(gòu)系統(tǒng)。如果您能夠傳遞屬性,那么您就可以在您的解決方案中使用面向?qū)ο蟮脑O(shè)計(jì)方式。所有面向?qū)ο蟮脑O(shè)計(jì)方式無不依靠不同的屬性來發(fā)揮功能,如果不能傳遞完整的對(duì)象 -- 包括實(shí)現(xiàn)和類型 -- 就會(huì)失去設(shè)計(jì)方式上所提供的優(yōu)點(diǎn)。?

?

安  全: RMI 使用 Java 內(nèi)置的安全機(jī)制保證下載執(zhí)行程序時(shí)用戶系統(tǒng)的安全。 RMI 使用專門為保護(hù)系統(tǒng)免遭惡意 小應(yīng)用程序 侵害而設(shè)計(jì)的安全管理程序,可保護(hù)您的系統(tǒng)和網(wǎng)絡(luò)免遭潛在的惡意下載程序的破壞。在情況嚴(yán)重時(shí),服務(wù)器可拒絕下載任何執(zhí)行程序。?

?

便于編寫和使用: RMI 使得 Java 遠(yuǎn)程服務(wù)程序和訪問這些服務(wù)程序的 Java 客戶程序的編寫工作變得輕松、簡(jiǎn)單。遠(yuǎn)程接口實(shí)際上就是 Java 接口。服務(wù)程序大約用三行指令宣布本身是服務(wù)程序,其它方面則與任何其它 Java 對(duì)象類似。這種簡(jiǎn)單方法便于快速編寫完整的分布式對(duì)象系統(tǒng)的服務(wù)程序,并快速地制做 軟件 的原型和早期版本,以便于進(jìn)行測(cè)試和評(píng)估。因?yàn)? RMI 程序編寫簡(jiǎn)單,所以維護(hù)也簡(jiǎn)單。?

?

可連接現(xiàn)有 / 原有的系統(tǒng): RMI 可通過 Java 的本機(jī)方法接口 JNI 與現(xiàn)有系統(tǒng)進(jìn)行進(jìn)行交互。利用 RMI JNI ,您就能用 Java 語言編寫客戶端程序,還能使用現(xiàn)有的服務(wù)器端程序。在使用 RMI/JNI 與現(xiàn)有服務(wù)器連接時(shí),您可以有選擇地用 Java 重新編寫服務(wù)程序的任何部分,并使新的程序充分發(fā)揮 Java 的功能。類似地, RMI 可利用 JDBC 、在不修改使用數(shù)據(jù)庫(kù)的現(xiàn)有非 Java 源代碼的前提下與現(xiàn)有關(guān)系數(shù)據(jù)庫(kù)進(jìn)行交互。?

?

編寫一次,到處運(yùn)行: RMI Java“ 編寫一次,到處運(yùn)行? 方法的一部分。任何基于 RMI 的系統(tǒng)均可 100% 地移植到任何 Java 虛擬機(jī) 上, RMI/JDBC 系統(tǒng)也不例外。如果使用 RMI/JNI 與現(xiàn)有系統(tǒng)進(jìn)行交互工作 , 則采用 JNI 編寫的代碼可與任何 Java 虛擬機(jī)進(jìn)行編譯、運(yùn)行。?

?

分布式垃圾收集: RMI 采用其分布式垃圾收集功能收集不再被網(wǎng)絡(luò)中任何客戶程序所引用的遠(yuǎn)程服務(wù)對(duì)象。與 Java? 虛擬機(jī)內(nèi)部的垃圾收集類似,分布式垃圾收集功能允許用戶根據(jù)自己的需要定義服務(wù)器對(duì)象,并且明確這些對(duì)象在不再被客戶機(jī)引用時(shí)會(huì)被刪除。?

?

并行計(jì)算: RMI 采用多 線程 處理方法,可使您的服務(wù)器利用這些 Java 線程更好地并行處理客戶端的請(qǐng)求。 Java 分布式計(jì)算解決方案: RMI JDK?1.1 開始就是 Java 平臺(tái)的核心部分,因此,它存在于任何一臺(tái) 1.1?Java 虛擬機(jī)中。所有 RMI 系統(tǒng)均采用相同的公開協(xié)議,所以,所有 Java? 系統(tǒng)均可直接相互對(duì)話,而不必事先對(duì)協(xié)議進(jìn)行轉(zhuǎn)換。

?

?

?

四、 RMI CORBA 的關(guān)系

?

RMI? 和? CORBA? 常被視為相互競(jìng)爭(zhēng)的技術(shù),因?yàn)閮烧叨继峁?duì)遠(yuǎn)程分布式對(duì)象的透明訪問。但這兩種技術(shù)實(shí)際上是相互補(bǔ)充的,一者的長(zhǎng)處正好可以彌補(bǔ)另一者的短處。 RMI? 和? CORBA? 的結(jié)合產(chǎn)生了? RMI-IIOP RMI-IIOP? 是企業(yè)服務(wù)器端? Java? 開發(fā)的基礎(chǔ)。 1997? 年, IBM? 和? Sun?Microsystems 啟動(dòng)了一項(xiàng)旨在促進(jìn)? Java? 作為企業(yè)開發(fā)技術(shù)的發(fā)展的合作計(jì)劃。兩家公司特別著力于如何將? Java? 用作服務(wù)器端語言,生成可以結(jié)合進(jìn)現(xiàn)有 體系結(jié)構(gòu) 的企業(yè)級(jí)代碼。所需要的就是一種遠(yuǎn)程傳輸技術(shù),它兼有? Java? 的? RMI Remote?Method?Invocation ,遠(yuǎn)程方法調(diào)用)較少的資源占用量和更成熟的? CORBA Common? Object ?Request?Broker? Architecture 公共對(duì)象請(qǐng)求代理體系結(jié)構(gòu) )技術(shù)的健壯性。出于這一需要, RMI-IIOP 問世了,它幫助將? Java? 語言推向了目前服務(wù)器端企業(yè)開發(fā)的主流語言的領(lǐng)先地位。?
(來源: sun matrix.org.cn

?

?

?

Java?RMI?簡(jiǎn)單示例一

?

以下用 一個(gè) 最簡(jiǎn)單的 Hello 的示例來 介紹 RMI 的應(yīng)用。

?

1 定義一個(gè)遠(yuǎn)程接口

?

I H ello .java 代碼如下

?

import ?java.rmi.Remote;

public ? interface ?IHello? extends ?Remote?{

public ?String?sayHello(String?name)? throws ?java.rmi.RemoteException;

}

?

?

?

?

?

2 實(shí)現(xiàn)遠(yuǎn)程的接口 (服務(wù)端就在此遠(yuǎn)程接口的實(shí)現(xiàn)類中)

?

HelloImpl.java 代碼如下

?

import ?java.rmi.RemoteException;
import ?java.rmi.server.UnicastRemoteObject;
public ? class ?HelloImpl? extends ?UnicastRemoteObject? implements ?IHello?{
????
// ?這個(gè)實(shí)現(xiàn)必須有一個(gè)顯式的構(gòu)造函數(shù),并且要拋出一個(gè)RemoteException異常??
???? protected ?HelloImpl()? throws ?RemoteException?{
????????
super ();
????}
????
/**
?????*?說明清楚此屬性的業(yè)務(wù)含義
?????
*/
????
private ? static ? final ? long ?serialVersionUID? = ? 4077329331699640331L ;
????
public ?String?sayHello(String?name)? throws ?RemoteException?{
????????
return ? " Hello? " ? + ?name? + ? " ?^_^? " ;
????}
????
public ? static ? void ?main(String[]?args)?{
????????
try ?{
????????????IHello?hello?
= ? new ?HelloImpl();
????????????java.rmi.Naming.rebind(
" rmi://localhost:1099/hello " ,?hello);
????????????System.out.print(
" Ready " );
????????}?
catch ?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
}

?

?

?


?

3 :新建 RMI 客戶端調(diào)用程序

?

Hello_RMI_Client .java 代碼如下

?

import ?java.rmi.Naming;
public ? class ?Hello_RMI_Client?{
????
public ? static ? void ?main(String[]?args)?{
????????
try ?{
????????????IHello?hello?
= ?(IHello)?Naming.lookup( " rmi://localhost:1099/hello " );
????????????????System.out.println(hello.sayHello(
" zhangxianxin " ));
????????}?
catch ?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
}

?

?

?

?

?

4 :編譯并運(yùn)行

?

4.1? javac 命令編譯 I H ello .java HelloImpl.java Hello_RMI_Client .java

?

>javac?*.java

?

4.2? rmic 命令 生成樁和框架文件

?

?>rmic? HelloImpl

?

成功執(zhí)行完上面的命令可以發(fā)現(xiàn) 生成 一個(gè) HelloImpl _stub.class 文件,如果 JDK 是使用 Java2SDK ,那么還可以發(fā)現(xiàn) 多出一個(gè) HelloImpl _Skel.class 文件。 如果服務(wù)端程序與客戶端程序在同一臺(tái)機(jī)器上并在同一目錄中,則可以省略掉接口實(shí)現(xiàn)類生成的樁和框架文件,但這就失去了使用 RMI 的意義,而如果要在不同的 JVM 上運(yùn)行時(shí),客戶端程序就必須得依靠服務(wù)端運(yùn)程方法實(shí)現(xiàn)的樁和框架文件以及接口類。

?

4.3? 運(yùn)行注冊(cè)程序 RMIRegistry ,必須在包含剛寫的類的目錄下運(yùn)行這個(gè)注冊(cè)程序。

?

>rmiregistry

?

注冊(cè)程序開始運(yùn)行了,不要管他,現(xiàn)在切換到另外一個(gè)控制臺(tái)運(yùn)行服務(wù)器 ?

?

4.4? 運(yùn)行服務(wù)器 HelloImpl

?

>java? HelloImpl

?

當(dāng)啟動(dòng)成功出現(xiàn) Ready......? 這個(gè)服務(wù)器就開始工作了,把接口的實(shí)現(xiàn)加載到內(nèi)存等待客戶端的聯(lián)接。現(xiàn)在切換到第三個(gè)控制臺(tái),啟動(dòng)我們的客戶端。

?

4.5? 啟動(dòng)客戶端 : 為了在其他的機(jī)器運(yùn)行客戶端程序你需要一個(gè)遠(yuǎn)程接口 ( I H ello .class)? 和一個(gè) stub( HelloImpl _Stub.class) 。?使用如下命令運(yùn)行客戶端

?

>java? Hello_RMI_Client

?

當(dāng)運(yùn)行成功會(huì)在控制臺(tái)打印: Hello?zhangxianxin?^_^

?

?

?

備注: 如果不想在控制臺(tái)上開啟 RMI 注冊(cè)程序 RMIRegistry 的話,可在 RMI 服務(wù)類程序中添加 LocateRegistry. createRegistry (1099); ? 如下所示:

?

修改后的 HelloImpl.java 代碼如下

?

import ?java.rmi.RemoteException;
import ?java.rmi.registry.LocateRegistry;
import ?java.rmi.server.UnicastRemoteObject;
public ? class ?HelloImpl? extends ?UnicastRemoteObject? implements ?IHello?{
????
// ?這個(gè)實(shí)現(xiàn)必須有一個(gè)顯式的構(gòu)造函數(shù),并且要拋出一個(gè)RemoteException異常??
???? protected ?HelloImpl()? throws ?RemoteException?{
????????
super ();
????}
????
????
private ? static ? final ? long ?serialVersionUID? = ? 4077329331699640331L ;
????
public ?String?sayHello(String?name)? throws ?RemoteException?{
????????
return ? " Hello? " ? + ?name? + ? " ?^_^? " ;
????}
????
public ? static ? void ?main(String[]?args)?{
????????
try ?{
????????????IHello?hello?
= ? new ?HelloImpl();
????????????LocateRegistry.createRegistry(
1099 );? // 加上此程序,就可以不要在控制臺(tái)上開啟RMI的注冊(cè)程序,1099是RMI服務(wù)監(jiān)視的默認(rèn)端口
????????????java.rmi.Naming.rebind( " rmi://localhost:1099/hello " ,?hello);
????????????System.out.print(
" Ready " );
????????}?
catch ?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
????}

?

?

?

?

?

Java?RMI?簡(jiǎn)單示例二

?

以下用 一個(gè)文件交換程序來介紹 RMI 的應(yīng)用。這個(gè)應(yīng)用允許客戶端從服務(wù)端交換 ( 或下載 ) 所有類型的文件。第一步是定義一個(gè)遠(yuǎn)程的接口,這個(gè)接口指定的簽名方法將被服務(wù)端提供和被客戶端調(diào)用。

?

1 .定義一個(gè)遠(yuǎn)程接口?

?

IFile Util .java 代碼如下 :

?

import ?java.rmi.Remote;

import ?java.rmi.RemoteException;

public ? interface ?IFileUtil? extends ?Remote?{

public ? byte []?downloadFile(String?fileName)? throws ?RemoteException;

}

?

接口 IFileDownload 提供了一個(gè) downloadFile 方法,然后返回一個(gè)相應(yīng)的文件數(shù)據(jù)。

?

?

?

2 .實(shí)現(xiàn)遠(yuǎn)程的接口?
FileImpl 繼承于 UnicastRemoteObject 類。這顯示出 FileImpl 類是用來創(chuàng)建一個(gè)單獨(dú)的、不能復(fù)制的、遠(yuǎn)程的對(duì)象,這個(gè)對(duì)象使用 RMI 默認(rèn)的基于 TCP 的通信方式。

?

File Util Impl.java 代碼如下 :

?

import ?java.io.BufferedInputStream;

import ?java.io.File;

import ?java.io.FileInputStream;

import ?java.rmi.RemoteException;

import ?java.rmi.server.UnicastRemoteObject;

public ? class ?FileUtilImpl? extends ?UnicastRemoteObject? implements ?IFileUtil?{

protected ?FileUtilImpl()? throws ?RemoteException?{

super ();

}

private ? static ? final ? long ?serialVersionUID? = ? 7594622080290821912L ;

public ? byte []?downloadFile(String?fileName)? throws ?RemoteException{

File?file?
= ? new ?File(fileName);

byte ?buffer[]? = ? new ? byte [( int )?file.length()];

int ?size? = ?buffer.length;

System.out.println(
" download?file?size?=? " + size? + " b " );

if (size > 1024 * 1024 * 10 ){ // 限制文件大小不能超過10M,文件太大可能導(dǎo)制內(nèi)存溢出!

throw ? new ?RemoteException( " Error:<The?File?is?too?big!> " );

}

try ?{

BufferedInputStream?input?
= ? new ?BufferedInputStream(

new ?FileInputStream(fileName));

input.read(buffer,?
0 ,?buffer.length);

input.close();

System.out.println(
" Info:<downloadFile()?hed?execute?successful!> " );

return ?buffer;

}?
catch ?(Exception?e)?{

System.out.println(
" FileUtilImpl:? " ? + ?e.getMessage());

e.printStackTrace();

return ? null ;

}

}

}

?

?

?

3 .編寫服務(wù)端?

?

1) 創(chuàng)建并安裝一個(gè) RMISecurityManager 實(shí)例。

?

2) 創(chuàng)建一個(gè)遠(yuǎn)程對(duì)象的實(shí)例。

?

3) 使用 RMI 注冊(cè)工具來注冊(cè)這個(gè)對(duì)象。

?

File Util Server.java? 代碼如下:

?

import ?java.rmi.Naming;

import ?java.rmi.RMISecurityManager;

public ? class ?FileUtilServer?{

public ? static ? void ?main(String?argv[])?{

try ?{

IFileUtil?file?
= ? new ?FileUtilImpl();

// LocateRegistry.createRegistry(1099);? // 加上此程序,就可以不要在控制臺(tái)上開啟RMI的注冊(cè)程序,1099是RMI服務(wù)監(jiān)視的默認(rèn)端口

Naming.rebind(
" rmi://127.0.0.1/FileUtilServer " ,?file);

System.out.print(
" Ready " );

}?
catch ?(Exception?e)?{

System.out.println(
" FileUtilServer:? " ? + ?e.getMessage());

e.printStackTrace();

}
}
}

?

聲明 Naming.rebind("rmi://127.0.0.1/FileUtilServer",?fi le )? 中假定了 RMI 注冊(cè)工具 (RMI?registry?) 使用并啟動(dòng)了 1099 端口。如果在其他端口運(yùn)行了 RMI 注冊(cè)工具,則必須在這個(gè)聲明中定義。例如,如果 RMI 注冊(cè)工具在 4500 端口運(yùn)行,則聲明應(yīng)為:   Naming.rebind("rmi://127.0.0.1 : 4500/FileUtilServer",?fi le )?

?

另外我們已經(jīng)同時(shí)假定了我們的服務(wù)端和 RMI 注冊(cè)工具是運(yùn)行在同一臺(tái)機(jī)器上的。否則需要修改 rebind 方法中的地址。

?

?

?

4 .編寫客戶端?

?

客戶端可以遠(yuǎn)程調(diào)用遠(yuǎn)程接口 (FileInterface) 中的任何一個(gè)方法。無論如何實(shí)現(xiàn),客戶端必須先從 RMI 注冊(cè)工具中獲取一個(gè)遠(yuǎn)程對(duì)象的引用。當(dāng)引用獲得后,方法 downloadFile 被調(diào)用。在執(zhí)行過程中,客戶端從命令行中獲得兩個(gè)參數(shù),第一個(gè)是要下載的文件名 , 第二個(gè)是要下載的機(jī)器的地址,在對(duì)應(yīng)地址的機(jī)器上運(yùn)行服務(wù)端。

?

File Util Client.java? 代碼如下:

?

import ?java.io.BufferedOutputStream;

import ?java.io.File;

import ?java.io.FileOutputStream;

import ?java.rmi.Naming;

public ? class ?FileUtilClient?{

public ? static ? void ?main(String?args[])?{

if ?(args.length? != ? 3 )?{

System.out.println(
" 第一個(gè)參數(shù):RMI服務(wù)的IP地址 " );

System.out.println(
" 第二個(gè)參數(shù):要下載的文件名 " );

System.out.println(
" 第三個(gè)參數(shù):要文件保存位置 " );

System.exit(
0 );

}

try ?{

String?name?
= ? " rmi:// " ? + ?args[ 0 ]? + ? " /FileUtilServer " ;

IFileUtil?fileUtil?
= ?(IFileUtil)?Naming.lookup(name);

byte []?filedata? = ?fileUtil.downloadFile(args[ 1 ]);

if (filedata == null ){

System.out.println(
" Error:<filedata?is?null!> " );

System.exit(
0 );

}

File?file?
= ? new ?File(args[ 2 ]);

System.out.println(
" file.getAbsolutePath()?=? " + file.getAbsolutePath());

BufferedOutputStream?output?
= ? new ?BufferedOutputStream(

new ?FileOutputStream(file.getAbsolutePath()));

output.write(filedata,?
0 ,?filedata.length);

output.flush();

output.close();

System.out.println(
" ~~~~~End~~~~~ " );

}?
catch ?(Exception?e)?{

System.err.println(
" FileUtilServer?exception:? " ? + ?e.getMessage());

e.printStackTrace();

}

}

}

?

?

?

5 .運(yùn)行程序
為了運(yùn)行程序,我們必須使用 rmic 來編譯生成 stubs skeletons:

?

>rmic?File Util Impl

?

這將會(huì)生成 File Util Impl_Stub.class File Util Impl_Skel.class 兩個(gè)文件。 stub 是客戶端的代理,而 skeleton 是服務(wù)端的框架。服務(wù)端和客戶端采用 javac 來編譯(如果服務(wù)端和客戶端在兩個(gè)不同的機(jī)器,則必須復(fù)制一個(gè) I File Util 接口)。

?

使用 rmiregistry 或者 start?rmiregistry? 命令來運(yùn)行 RMI 注冊(cè)工具到 window 系統(tǒng)默認(rèn)的端口上:
>?rmiregistry?portNumber
此處的 portNumber 為端口 , RMI 注冊(cè)工具運(yùn)行之后,需要運(yùn)行服務(wù) File Util Server ,因?yàn)? RMI 的安全機(jī)制將在服務(wù)端發(fā)生作用 , 所以必須增加一條安全策略:? grant{permission?java.security.AllPermission?"",?"";};

?

為了運(yùn)行服務(wù)端,需要有除客戶類 (File Util Client . class) 之外所有的類文件。確認(rèn)安全策略在 policy.txt 文件之后 , 使用如下命令來運(yùn)行服務(wù)器。
>?java?-Djava.security.policy=policy.txt?File Util Server

?

為了在其他的機(jī)器運(yùn)行客戶端程序 , 需要一個(gè)遠(yuǎn)程接口 ( I File Util .class) 和一個(gè) stub(File Util Impl_Stub.class) 。?使用如下命令運(yùn)行客戶端 :
>?java?File Util Client?fileName?machineName ?savePath
  這里 fileName 是要下載的文件名 ,machineName? 是要下載的文件所在的機(jī)器 ( 也是服務(wù)端所在的機(jī)器 ) ,savePath? 是要將下載過來的文件保存的路徑(帶文件名) 。如果全部通過的話 , 當(dāng)客戶端運(yùn)行后,則這個(gè)文件將被下載到本地。

?

?

?

?

?

?

?

?

?

?

?

Spring對(duì)RMI的支持

?

?

?

1. 使用 RMI 暴露服務(wù)

?

使用 Spring RMI 支持,你可以通過 RMI 基礎(chǔ)設(shè)施透明的暴露你的服務(wù)。設(shè)置好 Spring RMI 支持后,你會(huì)看到一個(gè)和遠(yuǎn)程 EJB 接口類似的配置,只是沒有對(duì)安全上下文傳遞和遠(yuǎn)程事務(wù)傳遞的標(biāo)準(zhǔn)支持。當(dāng)使用 RMI 調(diào)用器時(shí), Spring 對(duì)這些額外的調(diào)用上下文提供了鉤子,你可以在此插入安全框架或者定制的安全證書。

?


?

2.? 使用? RmiServiceExporter? 暴露服務(wù)

?

使用? RmiServiceExporter ,我們可以把 AccountService 對(duì)象的接口暴露成 RMI 對(duì)象。可以使用? RmiProxyFactoryBean? 或者在傳統(tǒng) RMI 服務(wù)中使用普通 RMI 來訪問該接口。 RmiServiceExporter? 顯式地支持使用 RMI 調(diào)用器暴露任何非 RMI 的服務(wù)。?

?

當(dāng)然,我們首先需要在 Spring?BeanFactory 中設(shè)置我們的服務(wù):?

?

< bean? id ="accountService" ?class ="example.AccountServiceImpl" >

????
<!-- ?any?additional?properties,?maybe?a?DAO?? -->

</ bean >

?

然后,我們將使用? RmiServiceExporter? 來暴露我們的服務(wù):

?

< bean? class ="org.springframework.remoting.rmi.RmiServiceExporter" >

<!-- ?does?not?necessarily?have?to?be?the?same?name?as?the?bean?to?be?exported? -->

< property? name ="serviceName" ?value ="AccountService" />

< property? name ="service" ?ref ="accountService" />

< property? name ="serviceInterface" ?value ="example.AccountService" />

<!-- ?defaults?to?1099? -->

< property? name ="registryPort" ?value ="1199" />

</ bean >

?

正如你所見,我們覆蓋了 RMI 注冊(cè)的端口號(hào)。通常,你的應(yīng)用服務(wù)也會(huì)維護(hù) RMI 注冊(cè),最好不要和它沖突。更進(jìn)一步來說,服務(wù)名是用來綁定下面的服務(wù)的。所以本例中,服務(wù)綁定在? rmi://HOST:1199/AccountService 。在客戶端我們將使用這個(gè) URL 來鏈接到服務(wù)。?

?

注意:我們省略了一個(gè)屬性,就是? servicePort ?屬性,它的默認(rèn)值為 0 。?這表示在服務(wù)通信時(shí)使用匿名端口。當(dāng)然如果你愿意的話,也可以指定一個(gè)不同的端口。 ?

?


?

3.? 在客戶端鏈接服務(wù)

?

我們的客戶端是一個(gè)使用 AccountService 來管理 account 的簡(jiǎn)單對(duì)象:?

?

public ? class ?SimpleObject?{

??
private ?AccountService?accountService;

??
public ? void ?setAccountService(AccountService?accountService)?{

????
this .accountService? = ?accountService;

??}

}

?

為了把服務(wù)連接到客戶端上,我們將創(chuàng)建另一個(gè)單獨(dú)的 bean 工廠,它包含這個(gè)簡(jiǎn)單對(duì)象和服務(wù)鏈接配置位:?

?

< bean? class ="example.SimpleObject" >

< property? name ="accountService" ?ref ="accountService" />

</ bean >

< bean? id ="accountService" ?class ="org.springframework.remoting.rmi.RmiProxyFactoryBean" >

< property? name ="serviceUrl" ?value ="rmi://HOST:1199/AccountService" />

< property? name ="serviceInterface" ?value ="example.AccountService" />

</ bean >

?

這就是我們?cè)诳蛻舳藶橹С诌h(yuǎn)程 account 服務(wù)所需要做的。 Spring 將透明的創(chuàng)建一個(gè)調(diào)用器并且通過 RmiServiceExporter 使得 account 服務(wù)支持遠(yuǎn)程服務(wù)。在客戶端,我們用 RmiProxyFactoryBean 連接它。

?

?

?

?

?

?

?

?

?

Spring對(duì)RMI支持的實(shí)際應(yīng)用實(shí)例

?

OMAS 系統(tǒng)中提供給業(yè)務(wù)系統(tǒng)的 RMI 客戶反饋服務(wù)的實(shí)現(xiàn)服務(wù)暴露是通過 R esource/modules/interfaces/spring-conf/ serviceContext.xml 配置文件實(shí)現(xiàn)的,而遠(yuǎn)程接口的實(shí)現(xiàn)類必須序列化(即實(shí)現(xiàn) Serializable 接口)。

?

R esource/modules/interfaces/spring-conf/ serviceContext.xml 的內(nèi)容如下:

?

<? xml?version="1.0"?encoding="UTF-8" ?>

<! DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd" >

< beans? default-autowire ="byName" ?default-lazy-init ="false" >

<!-- ?service實(shí)現(xiàn)類的配置? -->

< bean? id ="fbWebService" class ="com.ce.omas.interfaces.service.impl.FeedbackWebServiceImpl" ? />

< bean? class ="org.springframework.remoting.rmi.RmiServiceExporter" >

<!-- ?does?not?necessarily?have?to?be?the?same?name?as?the?bean?to?be?exported? -->

< property? name ="serviceName" ?value ="FeedbackRMIService" ? />

< property? name ="service" ?ref ="fbWebService" ? />

< property? name ="serviceInterface" value ="com.ce.omas.interfaces.service.IFeedbackWebService" ? />

<!-- ?<property?name="registryHost"?value="rmi://192.168.100.7"/>? -->

<!-- ?defaults?to?1099? -->

< property? name ="registryPort" ?value ="1199" ? />

</ bean >

</ beans >

?


?

對(duì)應(yīng)的暴露的服務(wù)接口如下:

?

public ? interface ?IFeedbackWebService?{

/**

?*?<b>方法用途和描述:</b>?客戶反饋RMI服務(wù)端接口方法<br>

?*?<b>方法的實(shí)現(xiàn)邏輯描述:</b>?通過RMI提供服務(wù),Spring支持的RMI遠(yuǎn)程調(diào)用

?*?
@param ?systemID?:?業(yè)務(wù)系統(tǒng)的唯一標(biāo)識(shí)

?*?
@param ?feedbackType?:用戶反饋的類型(1-系統(tǒng)BUG、2-系統(tǒng)易用性、3-客服人員態(tài)度、4-運(yùn)維人員態(tài)度、5-其他)

?*?
@param ?feedbackContent?:用戶反饋的正文內(nèi)容

?*?
@return ?反饋是否成功?true?|?false

?
*/

public ? boolean ?setFeedback(String?systemID,?FeedbackType?feedbackType,

String?feedbackContent)?
throws ?OMASServiceException?;

}

?

?

?

暴露的服務(wù)接口實(shí)現(xiàn)如下:

?

public ? class ?FeedbackWebServiceImpl? implements ?IFeedbackWebService,??Serializable?{

private ? static ?Log?_log? = ?LogFactory.getLog(FeedbackWebServiceImpl. class );

private ? static ? final ? long ?serialVersionUID? = ? - 5532505108644974594L ;

/**

?*?客戶反饋服務(wù)接口

?
*/

private ?IFeedbackOperationService?feedbackService;

/**

*?方法用途和描述:?注入運(yùn)營(yíng)模塊的添加客戶反饋的服務(wù)

*?
@param ?feedbackWebService?運(yùn)營(yíng)模塊服務(wù)

?
*/

public ? void ?setFeedbackService(IFeedbackOperationService?feedbackWebService)?{

_log.info(
" 注入運(yùn)營(yíng)模塊的添加客戶反饋的服務(wù) " );

this .feedbackService? = ?feedbackWebService;

}

/**

*?方法用途和描述:?外部接口子系統(tǒng)中添加客戶反饋的方法

*?
@param ?systemID?:業(yè)務(wù)系統(tǒng)ID

*?
@param ?feedbackType?:反饋類型

*?
@param ?feedbackContent?:反饋內(nèi)容

*?
@return ?操作是否成功?ture?or?false

?*?
@throws ?ServiceException?

?
*/

public ? boolean ?setFeedback(String?systemID,?FeedbackType?feedbackType,?String?feedbackContent)? throws ?OMASServiceException?{

_log.info(
" 進(jìn)入到外部接口的添加客戶反饋的方法 " );

if ?(BlankUtil.isBlank(systemID)? || ?BlankUtil.isBlank(feedbackType)

|| ?BlankUtil.isBlank(feedbackContent))?{

_log.error(
" 添加客戶反饋的接口參數(shù)為空! " );

throw ? new ?OMASServiceException( " omas.interfaces.001 " ); // 添加客戶反饋的接口參數(shù)為空

}

WebServiceFeedbackVO?vo?
= ? new ?WebServiceFeedbackVO();

vo.setFeedbackType(String.valueOf(feedbackType.getValue()));

vo.setFeedbackContent(feedbackContent);

vo.setSystemID(systemID);

_log.info(
" 調(diào)用運(yùn)營(yíng)子系統(tǒng)的添加客戶反饋的方法開始! " );

try ?{

if ?(feedbackService? == ? null )?{

_log.error(
" 運(yùn)營(yíng)模塊服務(wù)為空 " );

throw ? new ?OMASServiceException( " omas.interfaces.002 " ); // 運(yùn)營(yíng)模塊服務(wù)為空

}

feedbackService.addFeedbackOperation(vo);

}?
catch ?(ServiceException?e)?{

_log.error(
" 調(diào)用運(yùn)營(yíng)子系統(tǒng)的添加客戶反饋出現(xiàn)異常: " + e.getMessage());

if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_VO.equals(e.getMsgKey())){ // 客戶調(diào)用接口的對(duì)像為空

throw ? new ?OMASServiceException( " omas.interfaces.003 " );

}?
if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SYSTEMID.equals(e.getMsgKey())){ // 業(yè)務(wù)系統(tǒng)標(biāo)識(shí)ID為空

throw ? new ?OMASServiceException( " omas.omasservice.010 " );

}?
if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SIZE.equals(e.getMsgKey())){ // 非法的業(yè)務(wù)系統(tǒng)唯一標(biāo)識(shí)

throw ? new ?OMASServiceException( " omas.interfaces.004 " );

}?
if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_BASE.equals(e.getMsgKey())){ // 數(shù)據(jù)庫(kù)訪問出了一點(diǎn)小問題!

throw ? new ?OMASServiceException( " omas.interfaces.005 " );

}

throw ? new ?OMASServiceException( " omas.omasservice.000 " ); // 未捕獲到的異常信息!

}

return ? true ;

}

}

?

?

?

接口方法 setFeedback String,?FeedbackType,?String )的實(shí)現(xiàn)大家不用關(guān)心,其與 RMI 并無關(guān)系,只是一些純業(yè)務(wù)處理邏輯而已,要注意的是接口實(shí)現(xiàn)類必須實(shí)現(xiàn) ?IfeedbackWebService Serializable 接口。

?

?

?

在客戶本地的 omasservice.jar 包中客戶反饋的 RMI 客戶端的配置如下:

?

R esource/config/ omasrmi-client.xml

?

<? xml?version="1.0"?encoding="UTF-8" ?>

<! DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd" >

< beans? default-autowire ="byName" ?default-lazy-init ="true" >

< bean? id ="fbWebServiceProxy"

class
="org.springframework.remoting.rmi.RmiProxyFactoryBean" >

< property? name ="serviceUrl" >

< value > rmi://127.0.0.1:1199/FeedbackRMIService </ value >

</ property >

< property? name ="serviceInterface" >

< value > com.ce.omas.interfaces.service.IFeedbackWebService </ value >

</ property >

</ bean >

< bean? class ="com.ce.omas.omasservice.service.impl.FeedbackRMIClientImpl" >

< property? name ="feedbackWebService" ?ref ="fbWebServiceProxy" ? />

</ bean >

</ beans >

?

?

?

?

?

客戶端調(diào)用 RMI 服務(wù)的方法如下所示:

?

/**

*?方法用途和描述:?客戶反饋:通過RMI方法與OMAS通訊

*?方法的實(shí)現(xiàn)邏輯描述:

*?
@param ?feedbackType

*?
@param ?feedbackContent

*?
@return

*?
@throws ?OMASServiceException

*/

public ? static ? boolean ?setFeedback_RMIClient(String?systemID,?FeedbackType?feedbackType,?String?feedbackContent)? throws ?OMASServiceException?{

if ?(systemID? == ? null ? || ? "" .equals(systemID))?{

_log.error(
" 業(yè)務(wù)系統(tǒng)標(biāo)識(shí)<SystemID>為空或不是對(duì)象 " );

throw ? new ?OMASServiceException( " omas.omasservice.010 " );

}

String?rmiClientConfigFilePath?
= ?PropertyReader?.getValue(ConfigConstants.OMASSERVICE_CONFIG_PATH,?ConfigConstants.RMI_CLIENT_CONFIG_FILEPATH);

if ?(rmiClientConfigFilePath? == ? null ? || ? "" .equals(rmiClientConfigFilePath))?{

_log.error(
" 配置文件錯(cuò)誤:Key<rmiClientConfigFile>為空或不存在 " );

throw ? new ?OMASServiceException( " omas.omasservice.006 " );

}

_log.info(
" rmiClientConfigPath?=? " ? + ?rmiClientConfigFilePath);

ApplicationContext?context?
= ? null ;

try ?{

context? = ? new ?ClassPathXmlApplicationContext(rmiClientConfigFilePath);


}?
catch ?(Exception?e)?{

_log.error(
" 客戶反饋:解析rmi-config.xml文件時(shí)出現(xiàn)異常: " ? + ?e);

_log.info(
" rmi-config.xml文件路徑: " + rmiClientConfigFilePath);

throw ? new ?OMASServiceException( " omas.omasservice.007 " );

}

IFeedbackWebService?service?
= ? null ;

try ?{

service? = ?(IFeedbackWebService)?context?.getBean( " fbWebServiceProxy " );


}?
catch ?(Exception?e)?{

_log.error(
" 從Spring的RMI客戶端Bean配置文件獲取服務(wù)對(duì)象時(shí)出現(xiàn)異常: " ? + ?e);

throw ? new ?OMASServiceException( " omas.omasservice.009 " );

}

boolean ?bln? = ? service.setFeedback(systemID,?feedbackType,?feedbackContent);

_log.info(
" 反饋操作是否成功[true|false]: " ? + ?bln);

return ?bln;

}

?

在此客戶端調(diào)用的程序中,你要關(guān)注的主要是以上背景色標(biāo)志為黃色的相關(guān)代碼。

?

RMI 相關(guān)知識(shí)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 尉犁县| 抚宁县| 庆城县| 上栗县| 河西区| 大宁县| 育儿| 孟连| 巢湖市| 陆良县| 浦县| 武强县| 金沙县| 三河市| 巴南区| 教育| 客服| 祁门县| 吴川市| 廉江市| 沿河| 开鲁县| 新蔡县| 龙山县| 布拖县| 马龙县| 南乐县| 平邑县| 临高县| 丹棱县| 金沙县| 高安市| 新郑市| 云龙县| 泸溪县| 和平区| 明溪县| 乾安县| 周至县| 磐安县| 会宁县|