文章轉自:http://www.blogjava.net/zhenyu33154/articles/320245.html
?
RMI 全稱是 Remote?Method?Invocation -遠程方法調用, Java?RMI 在 JDK1.1 中實現的,其 威力就體現在它強大的開發分布式網絡應用的能力上 ,是純 Java 的網絡分布式應用系統的核心解決方案之一。 其實它可以被看作是 RPC 的 Java 版本。但是傳統 RPC 并不能很好地應用于分布式對象系統。而 Java?RMI? 則支持存儲于不同地址空間的程序級對象之間彼此進行通信,實現遠程對象之間的無縫遠程調用。
?
RMI 目前使用 Java 遠程消息交換協議 JRMP ( Java?Remote?Messaging?Protocol )進行通信 。 由于 JRMP 是專為 Java 對象制定的 , Java?RMI 具有 Java 的 "Write?Once,Run?Anywhere" 的優點,是分布式應用系統的百分之百純 Java 解決方案。用 Java?RMI 開發的應用系統可以部署在任何支持 JRE ( Java?Run?Environment?Java ,運行環境)的平臺上。但由于 JRMP 是專為 Java 對象制定的,因此, RMI 對于用非 Java 語言開發的應用系統的支持不足。不能與用非 Java 語言書寫的對象進行通信。
?
RMI 可利用標準 Java 本機方法接口 JNI 與現有的和原有的系統相連接。 RMI 還可利用標準 JDBC 包與現有的關系 數據庫 連接。 RMI/JNI 和 RMI/JDBC 相結合,可幫助您利用 RMI 與目前使用非 Java 語言的現有服務器進行通信,而且在您需要時可擴展 Java 在這些服務器上的使用。 RMI 可幫助您在擴展使用時充分利用 Java 的強大功能。
?
?
?
一、 RMI (遠程方法調用)的組成
?
一個正常工作的 RMI 系統由下面幾個部分組成:?
?
·
遠程服務的接口定義?
·
遠程服務接口的具體實現?
·
樁(
Stub
)和框架(
Skeleton
)文件?
·
一個運行遠程服務的服務器?
·
一個
RMI
命名服務,它允許
客戶端
去發現這個遠程服務?
·
類文件的提供者(一個
HTTP
或者
FTP
服務器)?
·
一個需要這個遠程服務的客戶端程序?
?
?
?
二、
RMI
(遠程方法調用)原理
示意圖
?
?
?
方法調用從客戶對象經占位程序( Stub) 、遠程引用層 (Remote?Reference?Layer) 和傳輸層( Transport?Layer )向下,傳遞給主機,然后再次經傳?輸層,向上穿過遠程調用層和骨干網( Skeleton ), 到達服務器對象。?占位程序扮演著遠程服務器對象的代理的角色,使該對象可被客戶激活。?遠程引用層處理語義、管理單一或多重對象的通信,決定調用是應發 往一個服務器還是多個。傳輸層管理實際的連接,并且追蹤可以接受方法調用的遠程對象。服務器端的骨干網完成對服務器對象實際的方法調用,并獲取返回值。返 回值向下經遠程引用層、服務器端的傳輸層傳遞回客戶端,再向上經傳輸層和遠程調用層返回。最后,占位程序獲得返回值。?
?
要完成以上步驟需要有以下幾個步驟:?
?
1、? 生成一個遠程接口?
?
2、? 實現遠程對象 ( 服務器端程序 )
?
3、? 生成占位程序和骨干網 ( 服務器端程序 )
?
4、? 編寫服務器程序?
?
5、? 編寫客戶程序?
?
6、? 注冊遠程對象?
?
7、? 啟動遠程對象?
?
?
?
三、 RMI (遠程方法調用)的優點?
?
從最基本的角度看, 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 與 CORBA 的關系
?
RMI?
和?
CORBA?
常被視為相互競爭的技術,因為兩者都提供對遠程分布式對象的透明訪問。但這兩種技術實際上是相互補充的,一者的長處正好可以彌補另一者的短處。
RMI?
和?
CORBA?
的結合產生了?
RMI-IIOP
,
RMI-IIOP?
是企業服務器端?
Java?
開發的基礎。
1997?
年,
IBM?
和?
Sun?Microsystems
啟動了一項旨在促進?
Java?
作為企業開發技術的發展的合作計劃。兩家公司特別著力于如何將?
Java?
用作服務器端語言,生成可以結合進現有
體系結構
的企業級代碼。所需要的就是一種遠程傳輸技術,它兼有?
Java?
的?
RMI
(
Remote?Method?Invocation
,遠程方法調用)較少的資源占用量和更成熟的?
CORBA
(
Common?
Object
?Request?Broker?
Architecture
,
公共對象請求代理體系結構
)技術的健壯性。出于這一需要,
RMI-IIOP
問世了,它幫助將?
Java?
語言推向了目前服務器端企業開發的主流語言的領先地位。?
(來源:
sun
;
matrix.org.cn
)
?
?
?
Java?RMI?簡單示例一
?
以下用 一個 最簡單的 Hello 的示例來 介紹 RMI 的應用。
?
1 : 定義一個遠程接口
?
I H ello .java 代碼如下 :
?
public ? interface ?IHello? extends ?Remote?{
public ?String?sayHello(String?name)? throws ?java.rmi.RemoteException;
}
?
?
?
?
?
2 : 實現遠程的接口 (服務端就在此遠程接口的實現類中)
?
HelloImpl.java 代碼如下 :
?
import ?java.rmi.server.UnicastRemoteObject;
public ? class ?HelloImpl? extends ?UnicastRemoteObject? implements ?IHello?{
???? // ?這個實現必須有一個顯式的構造函數,并且要拋出一個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();
????????????java.rmi.Naming.rebind( " rmi://localhost:1099/hello " ,?hello);
????????????System.out.print( " Ready


????????}? catch ?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
}
?
?
?
?
3 :新建 RMI 客戶端調用程序
?
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 :編譯并運行
?
4.1? 用 javac 命令編譯 I H ello .java 、 HelloImpl.java 、 Hello_RMI_Client .java
?
>javac?*.java
?
4.2? 用 rmic 命令 生成樁和框架文件
?
?>rmic? HelloImpl
?
成功執行完上面的命令可以發現 生成 一個 HelloImpl _stub.class 文件,如果 JDK 是使用 Java2SDK ,那么還可以發現 多出一個 HelloImpl _Skel.class 文件。 如果服務端程序與客戶端程序在同一臺機器上并在同一目錄中,則可以省略掉接口實現類生成的樁和框架文件,但這就失去了使用 RMI 的意義,而如果要在不同的 JVM 上運行時,客戶端程序就必須得依靠服務端運程方法實現的樁和框架文件以及接口類。
?
4.3? 運行注冊程序 RMIRegistry ,必須在包含剛寫的類的目錄下運行這個注冊程序。
?
>rmiregistry
?
注冊程序開始運行了,不要管他,現在切換到另外一個控制臺運行服務器 ?
?
4.4? 運行服務器 HelloImpl
?
>java? HelloImpl
?
當啟動成功出現 Ready......? 這個服務器就開始工作了,把接口的實現加載到內存等待客戶端的聯接。現在切換到第三個控制臺,啟動我們的客戶端。
?
4.5? 啟動客戶端 : 為了在其他的機器運行客戶端程序你需要一個遠程接口 ( I H ello .class)? 和一個 stub( HelloImpl _Stub.class) 。?使用如下命令運行客戶端
?
>java? Hello_RMI_Client
?
當運行成功會在控制臺打印: Hello?zhangxianxin?^_^
?
?
?
備注: 如果不想在控制臺上開啟 RMI 注冊程序 RMIRegistry 的話,可在 RMI 服務類程序中添加 LocateRegistry. createRegistry (1099); ? 如下所示:
?
修改后的 HelloImpl.java 代碼如下 :
?
import ?java.rmi.registry.LocateRegistry;
import ?java.rmi.server.UnicastRemoteObject;
public ? class ?HelloImpl? extends ?UnicastRemoteObject? implements ?IHello?{
???? // ?這個實現必須有一個顯式的構造函數,并且要拋出一個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 );? // 加上此程序,就可以不要在控制臺上開啟RMI的注冊程序,1099是RMI服務監視的默認端口
????????????java.rmi.Naming.rebind( " rmi://localhost:1099/hello " ,?hello);
????????????System.out.print( " Ready


????????}? catch ?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
????}
?
?
?
?
?
Java?RMI?簡單示例二
?
以下用 一個文件交換程序來介紹 RMI 的應用。這個應用允許客戶端從服務端交換 ( 或下載 ) 所有類型的文件。第一步是定義一個遠程的接口,這個接口指定的簽名方法將被服務端提供和被客戶端調用。
?
1 .定義一個遠程接口?
?
IFile
Util
.java
代碼如下
:
?
import ?java.rmi.RemoteException;
public ? interface ?IFileUtil? extends ?Remote?{
public ? byte []?downloadFile(String?fileName)? throws ?RemoteException;
}
?
接口 IFileDownload 提供了一個 downloadFile 方法,然后返回一個相應的文件數據。
?
?
?
2
.實現遠程的接口?
類
FileImpl
繼承于
UnicastRemoteObject
類。這顯示出
FileImpl
類是用來創建一個單獨的、不能復制的、遠程的對象,這個對象使用
RMI
默認的基于
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,文件太大可能導制內存溢出!
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 .編寫服務端?
?
( 1) 創建并安裝一個 RMISecurityManager 實例。
?
( 2) 創建一個遠程對象的實例。
?
( 3) 使用 RMI 注冊工具來注冊這個對象。
?
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);? // 加上此程序,就可以不要在控制臺上開啟RMI的注冊程序,1099是RMI服務監視的默認端口
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 注冊工具 (RMI?registry?) 使用并啟動了 1099 端口。如果在其他端口運行了 RMI 注冊工具,則必須在這個聲明中定義。例如,如果 RMI 注冊工具在 4500 端口運行,則聲明應為: Naming.rebind("rmi://127.0.0.1 : 4500/FileUtilServer",?fi le )?
?
另外我們已經同時假定了我們的服務端和 RMI 注冊工具是運行在同一臺機器上的。否則需要修改 rebind 方法中的地址。
?
?
?
4 .編寫客戶端?
?
客戶端可以遠程調用遠程接口 (FileInterface) 中的任何一個方法。無論如何實現,客戶端必須先從 RMI 注冊工具中獲取一個遠程對象的引用。當引用獲得后,方法 downloadFile 被調用。在執行過程中,客戶端從命令行中獲得兩個參數,第一個是要下載的文件名 , 第二個是要下載的機器的地址,在對應地址的機器上運行服務端。
?
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( " 第一個參數:RMI服務的IP地址 " );
System.out.println( " 第二個參數:要下載的文件名 " );
System.out.println( " 第三個參數:要文件保存位置 " );
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
.運行程序
為了運行程序,我們必須使用
rmic
來編譯生成
stubs
和
skeletons:
?
>rmic?File Util Impl
?
這將會生成 File Util Impl_Stub.class 和 File Util Impl_Skel.class 兩個文件。 stub 是客戶端的代理,而 skeleton 是服務端的框架。服務端和客戶端采用 javac 來編譯(如果服務端和客戶端在兩個不同的機器,則必須復制一個 I File Util 接口)。
?
使用
rmiregistry
或者
start?rmiregistry?
命令來運行
RMI
注冊工具到
window
系統默認的端口上:
>?rmiregistry?portNumber
此處的
portNumber
為端口
,
RMI
注冊工具運行之后,需要運行服務
File
Util
Server
,因為
RMI
的安全機制將在服務端發生作用
,
所以必須增加一條安全策略:?
grant{permission?java.security.AllPermission?"",?"";};
?
為了運行服務端,需要有除客戶類
(File
Util
Client
.
class)
之外所有的類文件。確認安全策略在
policy.txt
文件之后
,
使用如下命令來運行服務器。
>?java?-Djava.security.policy=policy.txt?File
Util
Server
?
為了在其他的機器運行客戶端程序
,
需要一個遠程接口
(
I
File
Util
.class)
和一個
stub(File
Util
Impl_Stub.class)
。?使用如下命令運行客戶端
:
>?java?File
Util
Client?fileName?machineName
?savePath
這里
fileName
是要下載的文件名
,machineName?
是要下載的文件所在的機器
(
也是服務端所在的機器
)
,savePath?
是要將下載過來的文件保存的路徑(帶文件名)
。如果全部通過的話
,
當客戶端運行后,則這個文件將被下載到本地。
?
?
?
?
?
?
?
?
?
?
?
Spring對RMI的支持
?
?
?
1. 使用 RMI 暴露服務
?
使用 Spring 的 RMI 支持,你可以通過 RMI 基礎設施透明的暴露你的服務。設置好 Spring 的 RMI 支持后,你會看到一個和遠程 EJB 接口類似的配置,只是沒有對安全上下文傳遞和遠程事務傳遞的標準支持。當使用 RMI 調用器時, Spring 對這些額外的調用上下文提供了鉤子,你可以在此插入安全框架或者定制的安全證書。
?
?
2.? 使用? RmiServiceExporter? 暴露服務
?
使用? RmiServiceExporter ,我們可以把 AccountService 對象的接口暴露成 RMI 對象。可以使用? RmiProxyFactoryBean? 或者在傳統 RMI 服務中使用普通 RMI 來訪問該接口。 RmiServiceExporter? 顯式地支持使用 RMI 調用器暴露任何非 RMI 的服務。?
?
當然,我們首先需要在 Spring?BeanFactory 中設置我們的服務:?
?
???? <!-- ?any?additional?properties,?maybe?a?DAO?? -->
</ bean >
?
然后,我們將使用?
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 注冊的端口號。通常,你的應用服務也會維護 RMI 注冊,最好不要和它沖突。更進一步來說,服務名是用來綁定下面的服務的。所以本例中,服務綁定在? rmi://HOST:1199/AccountService 。在客戶端我們將使用這個 URL 來鏈接到服務。?
?
注意:我們省略了一個屬性,就是? servicePort ?屬性,它的默認值為 0 。?這表示在服務通信時使用匿名端口。當然如果你愿意的話,也可以指定一個不同的端口。 ?
?
?
3.? 在客戶端鏈接服務
?
我們的客戶端是一個使用 AccountService 來管理 account 的簡單對象:?
?
?? private ?AccountService?accountService;
?? public ? void ?setAccountService(AccountService?accountService)?{
???? this .accountService? = ?accountService;
??}
}
?
為了把服務連接到客戶端上,我們將創建另一個單獨的 bean 工廠,它包含這個簡單對象和服務鏈接配置位:?
?
< 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 >
?
這就是我們在客戶端為支持遠程 account 服務所需要做的。 Spring 將透明的創建一個調用器并且通過 RmiServiceExporter 使得 account 服務支持遠程服務。在客戶端,我們用 RmiProxyFactoryBean 連接它。
?
?
?
?
?
?
?
?
?
Spring對RMI支持的實際應用實例
?
在 OMAS 系統中提供給業務系統的 RMI 客戶反饋服務的實現服務暴露是通過 R esource/modules/interfaces/spring-conf/ serviceContext.xml 配置文件實現的,而遠程接口的實現類必須序列化(即實現 Serializable 接口)。
?
R esource/modules/interfaces/spring-conf/ serviceContext.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 ="false" >
<!-- ?service實現類的配置? -->
< 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 >
?
?
對應的暴露的服務接口如下:
?
/**
?*?<b>方法用途和描述:</b>?客戶反饋RMI服務端接口方法<br>
?*?<b>方法的實現邏輯描述:</b>?通過RMI提供服務,Spring支持的RMI遠程調用
?*? @param ?systemID?:?業務系統的唯一標識
?*? @param ?feedbackType?:用戶反饋的類型(1-系統BUG、2-系統易用性、3-客服人員態度、4-運維人員態度、5-其他)
?*? @param ?feedbackContent?:用戶反饋的正文內容
?*? @return ?反饋是否成功?true?|?false
? */
public ? boolean ?setFeedback(String?systemID,?FeedbackType?feedbackType,
String?feedbackContent)? throws ?OMASServiceException?;
}
?
?
?
暴露的服務接口實現如下:
?
private ? static ?Log?_log? = ?LogFactory.getLog(FeedbackWebServiceImpl. class );
private ? static ? final ? long ?serialVersionUID? = ? - 5532505108644974594L ;
/**
?*?客戶反饋服務接口
? */
private ?IFeedbackOperationService?feedbackService;
/**
*?方法用途和描述:?注入運營模塊的添加客戶反饋的服務
*? @param ?feedbackWebService?運營模塊服務
? */
public ? void ?setFeedbackService(IFeedbackOperationService?feedbackWebService)?{
_log.info( " 注入運營模塊的添加客戶反饋的服務 " );
this .feedbackService? = ?feedbackWebService;
}
/**
*?方法用途和描述:?外部接口子系統中添加客戶反饋的方法
*? @param ?systemID?:業務系統ID
*? @param ?feedbackType?:反饋類型
*? @param ?feedbackContent?:反饋內容
*? @return ?操作是否成功?ture?or?false
?*? @throws ?ServiceException?
? */
public ? boolean ?setFeedback(String?systemID,?FeedbackType?feedbackType,?String?feedbackContent)? throws ?OMASServiceException?{
_log.info( " 進入到外部接口的添加客戶反饋的方法 " );
if ?(BlankUtil.isBlank(systemID)? || ?BlankUtil.isBlank(feedbackType)
|| ?BlankUtil.isBlank(feedbackContent))?{
_log.error( " 添加客戶反饋的接口參數為空! " );
throw ? new ?OMASServiceException( " omas.interfaces.001 " ); // 添加客戶反饋的接口參數為空
}
WebServiceFeedbackVO?vo? = ? new ?WebServiceFeedbackVO();
vo.setFeedbackType(String.valueOf(feedbackType.getValue()));
vo.setFeedbackContent(feedbackContent);
vo.setSystemID(systemID);
_log.info( " 調用運營子系統的添加客戶反饋的方法開始! " );
try ?{
if ?(feedbackService? == ? null )?{
_log.error( " 運營模塊服務為空 " );
throw ? new ?OMASServiceException( " omas.interfaces.002 " ); // 運營模塊服務為空
}
feedbackService.addFeedbackOperation(vo);
}? catch ?(ServiceException?e)?{
_log.error( " 調用運營子系統的添加客戶反饋出現異常: " + e.getMessage());
if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_VO.equals(e.getMsgKey())){ // 客戶調用接口的對像為空
throw ? new ?OMASServiceException( " omas.interfaces.003 " );
}? if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SYSTEMID.equals(e.getMsgKey())){ // 業務系統標識ID為空
throw ? new ?OMASServiceException( " omas.omasservice.010 " );
}? if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SIZE.equals(e.getMsgKey())){ // 非法的業務系統唯一標識
throw ? new ?OMASServiceException( " omas.interfaces.004 " );
}? if (ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_BASE.equals(e.getMsgKey())){ // 數據庫訪問出了一點小問題!
throw ? new ?OMASServiceException( " omas.interfaces.005 " );
}
throw ? new ?OMASServiceException( " omas.omasservice.000 " ); // 未捕獲到的異常信息!
}
return ? true ;
}
}
?
?
?
接口方法 setFeedback ( String,?FeedbackType,?String )的實現大家不用關心,其與 RMI 并無關系,只是一些純業務處理邏輯而已,要注意的是接口實現類必須實現 ?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 >
?
?
?
?
?
客戶端調用 RMI 服務的方法如下所示:
?
*?方法用途和描述:?客戶反饋:通過RMI方法與OMAS通訊
*?方法的實現邏輯描述:
*? @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( " 業務系統標識<SystemID>為空或不是對象 " );
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( " 配置文件錯誤: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文件時出現異常: " ? + ?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配置文件獲取服務對象時出現異常: " ? + ?e);
throw ? new ?OMASServiceException( " omas.omasservice.009 " );
}
boolean ?bln? = ? service.setFeedback(systemID,?feedbackType,?feedbackContent);
_log.info( " 反饋操作是否成功[true|false]: " ? + ?bln);
return ?bln;
}
?
在此客戶端調用的程序中,你要關注的主要是以上背景色標志為黃色的相關代碼。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
