文章轉(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)用)原理
示意圖
?
?
?
方法調(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 代碼如下 :
?
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.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 代碼如下 :
?
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.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.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.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.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.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ù):?
?
???? <!-- ?any?additional?properties,?maybe?a?DAO?? -->
</ bean >
?
然后,我們將使用?
RmiServiceExporter?
來暴露我們的服務(wù):
?
<!-- ?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ì)象:?
?
?? private ?AccountService?accountService;
?? public ? void ?setAccountService(AccountService?accountService)?{
???? this .accountService? = ?accountService;
??}
}
?
為了把服務(wù)連接到客戶端上,我們將創(chuàng)建另一個(gè)單獨(dú)的 bean 工廠,它包含這個(gè)簡(jiǎn)單對(duì)象和服務(wù)鏈接配置位:?
?
< 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)容如下:
?
<! 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ù)接口如下:
?
/**
?*?<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)如下:
?
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
?
<! 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)代碼。
?
更多文章、技術(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ì)您有幫助就好】元
