RMI實(shí)用教程
Java Remote Method Invocation ( RMI -- Java遠(yuǎn)程方法調(diào)用)允許您使用Java編寫(xiě)分布式對(duì)象。本文將介紹RMI的優(yōu)點(diǎn)以及如何將其連接到現(xiàn)有的和原有的系統(tǒng)中,以及與用Java 編寫(xiě)的組件的連接,同時(shí)給出了一個(gè)詳細(xì)的例子,可以給初學(xué)者提供一個(gè)學(xué)習(xí)范本。
一、RMI(遠(yuǎn)程方法調(diào)用)的組成
一個(gè)正常工作的RMI系統(tǒng)由下面幾個(gè)部分組成:
1、遠(yuǎn)程服務(wù)的接口定義
2、遠(yuǎn)程服務(wù)接口的具體實(shí)現(xiàn)
3、樁(Stub)和框架(Skeleton)文件
4、一個(gè)運(yùn)行遠(yuǎn)程服務(wù)的服務(wù)器
5、一個(gè)RMI命名服務(wù),它允許客戶(hù)端去發(fā)現(xiàn)這個(gè)遠(yuǎn)程服務(wù)
6、類(lèi)文件的提供者(一個(gè)HTTP或者FTP服務(wù)器)
7、一個(gè)需要這個(gè)遠(yuǎn)程服務(wù)的客戶(hù)端程序
二、RMI(遠(yuǎn)程方法調(diào)用)的工作原理
RMI系統(tǒng)結(jié)構(gòu),在客戶(hù)端和服務(wù)器端都有幾層結(jié)構(gòu)。
--------- ----------
| 客戶(hù) | | 服務(wù)器|
---------- ----------
| |
------------- ----------
| 占位程序 | | 骨干網(wǎng) |
-------------- -----------
| |
------------------------------------
| 遠(yuǎn) 程 引 用 層 |
------------------------------------
| |
------------------------------------
| 傳 輸 層 |
------------------------------------
方法調(diào)用從客戶(hù)對(duì)象經(jīng)占位程序(Stub)、遠(yuǎn)程引用層(Remote Reference Layer)和傳輸層(Transport Layer)向下,傳遞給主機(jī),然后再次經(jīng)傳 輸層,向上穿過(guò)遠(yuǎn)程調(diào)用層和骨干網(wǎng)(Skeleton),到達(dá)服務(wù)器對(duì)象。 占位程序扮演著遠(yuǎn)程服務(wù)器對(duì)象的代理的角色,使該對(duì)象可被客戶(hù)激活。 遠(yuǎn)程引用層處理語(yǔ)義、管理單一或多重對(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ù)器端的傳輸層傳遞回客戶(hù)端,再向上經(jīng)傳輸層和遠(yuǎn)程調(diào)用層返回。最后,占位程序獲得返回值。
要完成以上步驟需要有以下幾個(gè)步驟:
1、生成一個(gè)遠(yuǎn)程接口
2、實(shí)現(xiàn)遠(yuǎn)程對(duì)象(服務(wù)器端程序)
3、編寫(xiě)服務(wù)器程序 、注冊(cè)遠(yuǎn)程對(duì)象、啟動(dòng)遠(yuǎn)程對(duì)象
4、編寫(xiě)客戶(hù)程序
在JDK1.5之后,用java提供的API將會(huì)更加的簡(jiǎn)單,可以參照下面的例子;
三、例子
1、遠(yuǎn)程接口
接口名:com.liuxiang.rmi.download.IRMI
該接口定義了一個(gè)方法,用于提供遠(yuǎn)程服務(wù);
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 遠(yuǎn)程對(duì)象接口
* @author liuxiang
* 2007-8-30 下午09:39:15
*
*/
public interface IRMI extends Remote{
public Object invoke(ITask task) throws RemoteException;
}
2、實(shí)現(xiàn)遠(yuǎn)程對(duì)象(服務(wù)器端程序)
類(lèi)名:com.liuxiang.rmi.download.IRMIImpl
實(shí)現(xiàn)了遠(yuǎn)程接口定義的方法;該實(shí)現(xiàn)遠(yuǎn)程對(duì)象中,調(diào)用了傳入?yún)?shù)的task.doWork()方法,同時(shí)執(zhí)行一個(gè)本地調(diào)用,調(diào)用一個(gè)播放Mp3的代碼段ProcessCaller.callMp3();
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import com.liuxiang.callwindow.ProcessCaller;
/**
* 遠(yuǎn)程對(duì)象的實(shí)現(xiàn)
* @author liuxiang
* 2007-8-30 下午09:41:32
*
*/
public class IRMIImpl extends UnicastRemoteObject implements IRMI {
protected IRMIImpl() throws RemoteException {
super();
}
/**
*
*/
private static final long serialVersionUID = 6131922116577454476L;
/* (non-Javadoc)
* @see com.liuxiang.rmi.download.IRMI#invoke(com.liuxiang.rmi.download.ITask)
*/
public Object invoke(ITask task) throws RemoteException {
System.out.println("注意:這是一個(gè)遠(yuǎn)程調(diào)用");
Object obj = task.doWork();
System.out.println("調(diào)用ITask.doWork()方法的返回值:"+obj.toString());
//客戶(hù)端調(diào)用,可以在服務(wù)器端播放需要的音樂(lè)
ProcessCaller.callMp3();
return obj;
}
}
3、播放Mp3代碼段的源代碼如下:
/**
*
*/
package com.liuxiang.callwindow;
/**
* 在java中調(diào)用windows程序
* @author liuxiang 2007-8-30 下午10:26:20
*
*/
public class ProcessCaller {
/**
* 調(diào)用Windows程序
* 利用Windows Media Player播放mp3音樂(lè)
*/
public static void callMp3() {
Runtime ru = Runtime.getRuntime();
try {
// 調(diào)用播放器文件播放指定MP3
Process p1 = ru
.exec("C:\Program Files\Windows Media Player\wmplayer F:/music/lx/劉若英-后來(lái).mp3");
} catch (Exception e) {
}
}
/**
* @param args
*/
public static void main(String[] args) {
callMp3();
}
}
4、任務(wù)接口
該接口定義了遠(yuǎn)程方法需要傳遞的參數(shù)
/**
*
*/
package com.liuxiang.rmi.download;
import java.io.Serializable;
/**
* 任務(wù)接口
* @author liuxiang
* 2007-8-30 下午09:35:53
*
*/
public interface ITask extends Serializable{
public Object doWork();
}
5、任務(wù)接口實(shí)現(xiàn)
該實(shí)現(xiàn)定義了遠(yuǎn)程方法需要傳遞的參數(shù)
package com.liuxiang.rmi.download;
/**
* 任務(wù)實(shí)現(xiàn)類(lèi)
* @author liuxiang
* 2007-8-31 上午09:08:57
*
*/
public class TaskImpl implements ITask{
public Object doWork() {
System.out.println("當(dāng)前程序處于遠(yuǎn)程調(diào)用中");
return "動(dòng)態(tài)上載對(duì)象的返回值";
}
}
6、編寫(xiě)服務(wù)器程序 、注冊(cè)遠(yuǎn)程對(duì)象、啟動(dòng)遠(yuǎn)程對(duì)象
代碼:遠(yuǎn)程對(duì)象的注冊(cè)類(lèi) 該類(lèi)應(yīng)該在服務(wù)器端執(zhí)行 執(zhí)行之后, 該機(jī)器將變?yōu)镽MI服務(wù)器 客戶(hù)端可以通過(guò)正確的url來(lái)訪問(wèn);服務(wù)器上的遠(yuǎn)程對(duì)象;執(zhí)行對(duì)外報(bào)露的方法
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
/**
* 遠(yuǎn)程對(duì)象的注冊(cè)類(lèi) 該類(lèi)應(yīng)該在服務(wù)器端執(zhí)行 執(zhí)行之后,
* 該機(jī)器將變?yōu)镽MI服務(wù)器 客戶(hù)端可以通過(guò)正確的url來(lái)訪問(wèn)
* 服務(wù)器上的遠(yuǎn)程對(duì)象;執(zhí)行對(duì)外報(bào)露的方法
*
* @author liuxiang 2007-8-30 下午09:44:54
*
*/
public class RMIServer {
/**
* 如果沒(méi)有創(chuàng)建一個(gè)Registry,Naming是不會(huì)幫助你創(chuàng)建的。 還是自己手工創(chuàng)建的比較的好
*
* 盡量用下面的自己封裝的bind方法來(lái)注冊(cè)遠(yuǎn)程對(duì)象
*
* @throws Exception
*/
public static void registRemoteObject() throws Exception {
IRMIImpl impl = new IRMIImpl();
Naming.rebind("rmi://219.233.8.97:1111/mytask", impl);
System.out.println("bound success!");
}
/**
* 創(chuàng)建一個(gè)Registry對(duì)象
*
* @return 返回一個(gè)Registry對(duì)象
*/
private static Registry createRegistry() {
Registry registry = null;
int port = 1111;
try {
registry = LocateRegistry.getRegistry(port);
registry.list();
System.out.println("Register the exist server!");
} catch (final Exception e) {
try {
registry = LocateRegistry.createRegistry(port);
System.out.println("Register the exist server!port=" + port);
} catch (final Exception ee) {
ee.printStackTrace();
}
}
return registry;
}
/**
* 將對(duì)象注冊(cè)到rmi服務(wù)器上
*/
public static void bind() {
Registry registry = null;
registry = createRegistry();
try {
IRMIImpl impl = new IRMIImpl();
registry.rebind("mytask", impl);
System.out.println("mytask server start!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
try {
bind();
} catch (Exception e) {
e.printStackTrace();
}
}
}
運(yùn)行該服務(wù)端代碼后,將會(huì)注冊(cè)一個(gè)遠(yuǎn)程服務(wù)對(duì)象,通過(guò)恰當(dāng)?shù)腢RL可以訪問(wèn)遠(yuǎn)程對(duì)象中的方法;運(yùn)行后的結(jié)果如下:
Register the exist server!port=1111
mytask server start!
7、編寫(xiě)客戶(hù)程序
代碼首先獲取一個(gè)遠(yuǎn)程對(duì)象,然后如同本地調(diào)用一樣,調(diào)用遠(yuǎn)程對(duì)象中的方法。
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Naming;
/**
* @author liuxiang
* 2007-8-30 下午09:47:41
*
*/
public class RMIClient {
/**
* 調(diào)用遠(yuǎn)程對(duì)象中的方法
* @throws Exception
*/
public static void getRemoteObject() throws Exception{
IRMI obj = (IRMI)Naming.lookup("rmi://localhost:1111/mytask"); //得到遠(yuǎn)程發(fā)布的服務(wù)
TaskImpl task = new TaskImpl();
Object result = obj.invoke(task); //調(diào)用遠(yuǎn)程服務(wù)的方法
System.out.println(result.toString());
}
/**
* @param args
*/
public static void main(String[] args) {
try {
getRemoteObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后,運(yùn)行客戶(hù)端代碼,可以看到控制臺(tái)分別輸出了如下的內(nèi)容:
Server端輸出了更多的內(nèi)容,如下:
Register the exist server!port=1111
mytask server start!
注意:這是一個(gè)遠(yuǎn)程調(diào)用
當(dāng)前程序處于遠(yuǎn)程調(diào)用中
調(diào)用ITask.doWork()方法的返回值:動(dòng)態(tài)上載對(duì)象的返回值
同時(shí)可以看到,會(huì)調(diào)用了Windows的Mediaplay播放本地磁盤(pán)的Mp3歌曲;
Client端輸入了如下內(nèi)容:
動(dòng)態(tài)上載對(duì)象的返回值
四、RMI介紹
RMI(Remote Method Invocation,遠(yuǎn)程方法調(diào)用)是用Java在JDK1.1中實(shí)現(xiàn)的,它大大增強(qiáng)了Java開(kāi)發(fā)分布式應(yīng)用的能力。Java作為一種風(fēng)靡一時(shí)的網(wǎng)絡(luò)開(kāi)發(fā)語(yǔ)言,其巨大的威力就體現(xiàn)在它強(qiáng)大的開(kāi)發(fā)分布式網(wǎng)絡(luò)應(yīng)用的能力上,而RMI就是開(kāi)發(fā)百分之百純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ì)象之間的無(wú)縫遠(yuǎn)程調(diào)用。
RMI目前使用Java遠(yuǎn)程消息交換協(xié)議JRMP(Java Remote Messaging Protocol)進(jìn)行通信。JRMP是專(zhuān)為Java的遠(yuǎn)程對(duì)象制定的協(xié)議。因此,Java RMI具有Java的"Write Once,Run Anywhere"的優(yōu)點(diǎn),是分布式應(yīng)用系統(tǒng)的百分之百純Java解決方案。用Java RMI開(kāi)發(fā)的應(yīng)用系統(tǒng)可以部署在任何支持JRE(Java Run Environment Java,運(yùn)行環(huán)境)的平臺(tái)上。但由于JRMP是專(zhuān)為Java對(duì)象制定的,因此,RMI對(duì)于用非Java語(yǔ)言開(kāi)發(fā)的應(yīng)用系統(tǒng)的支持不足。不能與用非Java語(yǔ)言書(shū)寫(xiě)的對(duì)象進(jìn)行通信。
RMI為采用Java對(duì)象的分布式計(jì)算提供了簡(jiǎn)單而直接的途徑。這些對(duì)象可以是新的Java對(duì)象,也可以是圍繞現(xiàn)有API的簡(jiǎn)單的Java包裝程序。Java體現(xiàn)了“編寫(xiě)一次就能在任何地方運(yùn)行的模式。而RMI可將Java模式進(jìn)行擴(kuò)展,使之可在任何地方運(yùn)行”。
因?yàn)镽MI是以Java為核心的,所以,它將Java的安全性和可移植性等強(qiáng)大功能帶給了分布式計(jì)算。您可將代理和梢?務(wù)邏輯等屬性移動(dòng)到網(wǎng)絡(luò)中最合適的地方。如果您要擴(kuò)展Java在系統(tǒng)中的使用,RMI將使您充分利用其強(qiáng)大功能。
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語(yǔ)言的現(xiàn)有服務(wù)器進(jìn)行通信,而且在您需要時(shí)可擴(kuò)展Java在這些服務(wù)器上的使用。RMI可幫助您在擴(kuò)展使用時(shí)充分利用Java的強(qiáng)大功能。
posted on 2009-08-11 17:50 小卓 閱讀(146) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): EJB