Java RMI远程方法调用详解
生活随笔
收集整理的這篇文章主要介紹了
Java RMI远程方法调用详解
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Java RMI遠(yuǎn)程方法調(diào)用詳解
? ? 【尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處】http://blog.csdn.net/guyuealian/article/details/51992182一、Java RMI機(jī)制: ? ?? ??遠(yuǎn)程方法調(diào)用RMI(Remote Method Invocation),是允許運(yùn)行在一個(gè)Java虛擬機(jī)的對(duì)象調(diào)用運(yùn)行在另一個(gè)Java虛擬機(jī)上的對(duì)象的方法。 這兩個(gè)虛擬機(jī)可以是運(yùn)行在相同計(jì)算機(jī)上的不同進(jìn)程中,也可以是運(yùn)行在網(wǎng)絡(luò)上的不同計(jì)算機(jī)中。? ? ? ?Java RMI:Java遠(yuǎn)程方法調(diào)用,即Java RMI(Java Remote Method Invocation)是Java編程語(yǔ)言里,一種用于實(shí)現(xiàn)遠(yuǎn)程過(guò)程調(diào)用的應(yīng)用程序編程接口。它使客戶機(jī)上運(yùn)行的程序可以調(diào)用遠(yuǎn)程服務(wù)器上的對(duì)象。遠(yuǎn)程方法調(diào)用特性使Java編程人員能夠在網(wǎng)絡(luò)環(huán)境中分布操作。RMI全部的宗旨就是盡可能簡(jiǎn)化遠(yuǎn)程接口對(duì)象的使用。? ? ? ?而RPC是遠(yuǎn)程過(guò)程調(diào)用(Remote Procedure Call)可以用于一個(gè)進(jìn)程調(diào)用另一個(gè)進(jìn)程(很可能在另一個(gè)遠(yuǎn)程主機(jī)上)中的過(guò)程,從而提供了過(guò)程的分布能力。Java 的 RMI 則在 RPC 的基礎(chǔ)上向前又邁進(jìn)了一步,即提供分布式對(duì)象間的通訊。?(1)RMI框架? ? ? ?【參考資料】? ? ?? ?《Java網(wǎng)絡(luò)編程精解》孫衛(wèi)琴,這本書(shū)適合入門學(xué)習(xí)RMI框架基礎(chǔ) ??? ? ? ? ?http://wenku.baidu.com/view/90171bd03186bceb19e8bbdc.html?from=search? ? ? ?RMI框架封裝了所有底層通信細(xì)節(jié),并且解決了編組、分布式垃圾收集、安全檢查和并發(fā)性等通用問(wèn)題。有了現(xiàn)成的框架,開(kāi)發(fā)人員就只需專注于開(kāi)發(fā)與特定問(wèn)題領(lǐng)域相關(guān)的各種本地對(duì)象和遠(yuǎn)程對(duì)象。
? ? ? 要了解RMI原理,先了解一下Stub和Skeleton兩個(gè)概念。
(2)Stub和Skeleton?? ? ?RMI框架采用代理來(lái)負(fù)責(zé)客戶與遠(yuǎn)程對(duì)象之間通過(guò)Socket進(jìn)行通信的細(xì)節(jié)。RMI框架為遠(yuǎn)程對(duì)象分別生成了客戶端代理和服務(wù)器端代理。位于客戶端的代理類稱為存根(Stub),位于服務(wù)器端的代理類稱為骨架(Skeleton)。
? ? 【相關(guān)資料】? ? ?《RMI(Remote Method Invocation)初窺門徑》 http://blog.csdn.net/smcwwh/article/details/7080997?? ? ?stub(存根)和skeleton(骨架)在RMI中充當(dāng)代理角色,在現(xiàn)實(shí)開(kāi)發(fā)中主要是用來(lái)隱藏系統(tǒng)和網(wǎng)絡(luò)的的差異, 這一部分的功能在RMI開(kāi)發(fā)中對(duì)程序員是透明的。Stub為客戶端編碼遠(yuǎn)程命令并把他們發(fā)送到服務(wù)器。而Skeleton則是把遠(yuǎn)程命令解碼,調(diào)用服務(wù)端的遠(yuǎn)程對(duì)象的方法,把結(jié)果在編碼發(fā)給stub,然后stub再解碼返回調(diào)用結(jié)果給客戶端。? ? ?RMI遠(yuǎn)程過(guò)程調(diào)用的實(shí)現(xiàn)過(guò)程如下圖所示:
? ? ?RMI 框架的基本原理大概如下圖,應(yīng)用了代理模式來(lái)封裝了本地存根與真實(shí)的遠(yuǎn)程對(duì)象進(jìn)行通信的細(xì)節(jié)
【參考資料】? ? 《Java RMI 框架(遠(yuǎn)程方法調(diào)用)》http://haolloyin.blog.51cto.com/1177454/332426 ?? ? 《?java RMI原理詳解》 http://blog.csdn.net/xinghun_4/article/details/45787549
二、Java RMI 簡(jiǎn)單示例
? ? 別急,慢慢分析~具體代碼在下面,附例子代碼下載:http://download.csdn.net/detail/guyuealian/9583633大致說(shuō)來(lái),創(chuàng)建一個(gè)RMI應(yīng)用包括以下步驟:? ? ? (1)創(chuàng)建遠(yuǎn)程接口:繼承java.rmi.Remote接口。? ? ? (2)創(chuàng)建遠(yuǎn)程類:實(shí)現(xiàn)遠(yuǎn)程接口。
? ? ? (3)創(chuàng)建服務(wù)器程序:創(chuàng)建遠(yuǎn)程對(duì)象,通過(guò)createRegistry()方法注冊(cè)遠(yuǎn)程對(duì)象。并通過(guò)bind或者rebind方法,把遠(yuǎn)程對(duì)象綁定到指定名稱空間(URL)中。
? ? ? (4)創(chuàng)建客戶程序:通過(guò) lookup()方法查找遠(yuǎn)程對(duì)象,進(jìn)行遠(yuǎn)程方法調(diào)用??? ? 下面具體分析每個(gè)步驟:
(1)創(chuàng)建遠(yuǎn)程接口:繼承java.rmi.Remote接口。? ? ? ?遠(yuǎn)程接口中聲明了可以被客戶程序訪問(wèn)的遠(yuǎn)程方法。RMI規(guī)范要求遠(yuǎn)程對(duì)象所屬的類實(shí)現(xiàn)一個(gè)遠(yuǎn)程接口,并且遠(yuǎn)程接口符合以下條件:
? ? ? ?(a)直接或間接繼承java.rmi.Remote接口。? ? ? ?(b)接口中的所有方法聲明拋出java.rmi.RemoteException。
(2)創(chuàng)建遠(yuǎn)程類:實(shí)現(xiàn)遠(yuǎn)程接口。? ? ? ??遠(yuǎn)程類就是遠(yuǎn)程對(duì)象所屬的類。RMI規(guī)范要求遠(yuǎn)程類必須實(shí)現(xiàn)一個(gè)遠(yuǎn)程接口。此外,為了使遠(yuǎn)程類的實(shí)例變成能為遠(yuǎn)程客戶提供服務(wù)的遠(yuǎn)程對(duì)象,可通過(guò)以下兩種途徑之一把它導(dǎo)出(export)為遠(yuǎn)程對(duì)象:
? ? ? ?(a)導(dǎo)出為遠(yuǎn)程對(duì)象的第一種方式:使遠(yuǎn)程類實(shí)現(xiàn)遠(yuǎn)程接口時(shí),同時(shí)繼承java.rmi.server.UnicastRemoteObject類,并且遠(yuǎn)程類的構(gòu)造方法必須聲明拋出RemoteException。這是最常用的方式,下面的本例子就采取這種方式。public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface? ? ? ?(b)導(dǎo)出為遠(yuǎn)程對(duì)象的第二種方式:如果一個(gè)遠(yuǎn)程類已經(jīng)繼承了其他類,無(wú)法再繼承UnicastRemoteObject類,那么可以在構(gòu)造方法中調(diào)用UnicastRemoteObject類的靜態(tài)exportObject()方法,同樣,遠(yuǎn)程類的構(gòu)造方法也必須聲明拋出RemoteException。public class RemoteImpl?extends OtherClass implements RemoteInterface{private String name;public RemoteImpl?(String name)throws RemoteException{this.name=name;UnicastRemoteObject.exportObject(this,0);} ? ? ? ? 在構(gòu)造方法RemoteImpl?中調(diào)用了UnicastRemoteObject.exportObject(this,0)方法,將自身導(dǎo)出為遠(yuǎn)程對(duì)象。? ? ? ??exportObject()是UnicastRemoteObject的靜態(tài)方法,源碼是: protected UnicastRemoteObject(int port) throws RemoteException{this.port = port;exportObject((Remote) this, port);} public static Remote exportObject(Remote obj, int port)throws RemoteException{return exportObject(obj, new UnicastServerRef(port));}? ? ? ?exportObject(Remote obj, int port),該方法負(fù)責(zé)把參數(shù)obj指定的對(duì)象導(dǎo)出為遠(yuǎn)程對(duì)象,使它具有相應(yīng)的存根(Stub),并監(jiān)聽(tīng)遠(yuǎn)程客戶的方法調(diào)用請(qǐng)求;參數(shù)port指導(dǎo)監(jiān)聽(tīng)的端口,如果值為0,表示監(jiān)聽(tīng)任意一個(gè)匿名端口。
(3)創(chuàng)建服務(wù)器程序:創(chuàng)建遠(yuǎn)程對(duì)象,在rmiregistry注冊(cè)表中注冊(cè)遠(yuǎn)程對(duì)象,并綁定到指定的URL中。? ? ? ?RMI采用一種命名服務(wù)機(jī)制來(lái)使得客戶程序可以找到服務(wù)器上的一個(gè)遠(yuǎn)程對(duì)象。在JDK的安裝目錄的bin子目錄下有一個(gè)rmiregistry.exe程序,它是提供命名服務(wù)的注冊(cè)表程序。
? ? ? 服務(wù)器程序的一大任務(wù)就是向rmiregistry注冊(cè)表注冊(cè)遠(yuǎn)程對(duì)象。從JDK1.3以上版本開(kāi)始,RMI的命名服務(wù)API被整合到JNDI(Java Naming and Directory Interface,Java名字與目錄接口)中。在JNDI中,javax.naming.Context接口聲明了注冊(cè)、查找,以及注銷對(duì)象的方法:
? ? 【1】 bind(String name,Object obj):注冊(cè)對(duì)象,把對(duì)象與一個(gè)名字name綁定,這里的name其實(shí)就是URL格式。如果該名字已經(jīng)與其它對(duì)象綁定,就會(huì)拋出NameAlreadyBoundException。
? ? 【2】rebind(String name,Object obj):注冊(cè)對(duì)象,把對(duì)象與一個(gè)名字綁定。如果該名字已經(jīng)與其它對(duì)象綁定,不會(huì)拋出NameAlreadyBoundException,而是把當(dāng)前參數(shù)obj指定的對(duì)象覆蓋原先的對(duì)象。
? ? 【3】 lookup(String name):查找對(duì)象,返回與參數(shù)name指定的名字所綁定的對(duì)象。
? ? 【4】unbind(String name):注銷對(duì)象,取消對(duì)象與名字的綁定。? ? ?注冊(cè)一個(gè)遠(yuǎn)程對(duì)象remoteObj2?關(guān)鍵代碼如下:RemoteInterface remoteObj2 = new RemoteImpl();// 創(chuàng)建遠(yuǎn)程對(duì)象 Context namingContext = new InitialContext();// 初始化命名內(nèi)容 LocateRegistry.createRegistry(8892);// 在本地主機(jī)上創(chuàng)建和導(dǎo)出注冊(cè)表實(shí)例,并在指定的端口上接受請(qǐng)求 namingContext.rebind("rmi://localhost:8892/RemoteObj2", remoteObj2);// 注冊(cè)對(duì)象,即把對(duì)象與一個(gè)名字綁定。? ? ?但在JDK1.3版本或更低的版本,需要使用java.rmi.Naming來(lái)注冊(cè)遠(yuǎn)程對(duì)象,注冊(cè)代碼代替如下:RemoteInterface remoteObj = new RemoteImpl(); LocateRegistry.createRegistry(8891); Naming.rebind("rmi://localhost:8891/RemoteObj", remoteObj);(4)創(chuàng)建客戶程序:通過(guò) lookup()方法查找遠(yuǎn)程對(duì)象,進(jìn)行遠(yuǎn)程方法調(diào)用關(guān)鍵代碼如下:Context namingContext = new InitialContext();// 初始化命名內(nèi)容 RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//獲得遠(yuǎn)程對(duì)象的存根對(duì)象 System.out.println(RmObj2.doSomething());//通過(guò)遠(yuǎn)程對(duì)象,調(diào)用doSomething方法? ? ?在JDK1.3版本或更低的版本,如下方式調(diào)用;RemoteInterface RmObj = (RemoteInterface) Naming.lookup("rmi://localhost:8891/RemoteObj"); System.out.println(RmObj.doSomething());例子具體代碼:? ?1. 創(chuàng)建遠(yuǎn)程接口:繼承java.rmi.Remote接口。
package rmi; import java.rmi.Remote; import java.rmi.RemoteException; //聲明一個(gè)遠(yuǎn)程接口RemoteInterface,該接口必須繼承Remote接口 //接口中需要被遠(yuǎn)程調(diào)用的方法,必須拋出RemoteException異常 public interface RemoteInterface extends Remote {// 聲明一個(gè)doSomething方法public String doSomething() throws RemoteException;// 聲明一個(gè)計(jì)算方法Calculatepublic int Calculate(int num1, int num2) throws RemoteException; }? ? ? ?在Java中,只要一個(gè)類extends了java.rmi.Remote接口,即可成為存在于服務(wù)器端的遠(yuǎn)程對(duì)象, 供客戶端訪問(wèn)并提供一定的服務(wù)。JavaDoc描述:Remote 接口用于標(biāo)識(shí)其方法可以從非本地虛擬機(jī)上調(diào)用的接口。任何遠(yuǎn)程對(duì)象都必須直接或間接實(shí)現(xiàn)此接口。只有在“遠(yuǎn)程接口” ?(擴(kuò)展 java.rmi.Remote 的接口)中指定的這些方法才可被遠(yuǎn)程調(diào)用。注意:接口中需要被遠(yuǎn)程調(diào)用的方法,必須拋出RemoteException異常。? ? ? 2. 創(chuàng)建遠(yuǎn)程類:實(shí)現(xiàn)遠(yuǎn)程接口package rmi; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; //實(shí)現(xiàn)遠(yuǎn)程接口RemoteInterface,并繼承UnicastRemoteObject //注意RemoteObject這個(gè)類,實(shí)現(xiàn)了Serializable, Remote這兩個(gè)接口 public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface {// 這個(gè)實(shí)現(xiàn)必須有一個(gè)顯式的構(gòu)造函數(shù),并且要拋出一個(gè)RemoteException異常public RemoteImpl() throws RemoteException {}// 實(shí)現(xiàn)doSomething方法public String doSomething() throws RemoteException {return "OK ,You can do......";}// 實(shí)現(xiàn)Calculate方法,返回計(jì)算結(jié)果public int Calculate(int num1, int num2) throws RemoteException {return (num1 + num2);} }? ? ? 遠(yuǎn)程對(duì)象必須實(shí)現(xiàn)java.rmi.server.UniCastRemoteObject類,該類的構(gòu)造函數(shù)中將生成stub和skeleton,?這樣才能保證客戶端訪問(wèn)獲得遠(yuǎn)程對(duì)象時(shí),該遠(yuǎn)程對(duì)象將會(huì)把自身的一個(gè)拷貝以Socket的形式傳輸給客戶端,此時(shí)客戶端所獲得的這個(gè)拷貝稱為Stub(存根),?而服務(wù)器端本身已存在的遠(yuǎn)程對(duì)象則稱之為Skeleton(骨架)。其實(shí)此時(shí)的存根是客戶端的一個(gè)代理,用于與服務(wù)器端的通信,??而骨架也可認(rèn)為是服務(wù)器端的一個(gè)代理,用于接收客戶端的請(qǐng)求之后調(diào)用遠(yuǎn)程方法來(lái)響應(yīng)客戶端的請(qǐng)求。? ? ?3.創(chuàng)建服務(wù)器程序:在rmiregistry注冊(cè)表中注冊(cè)遠(yuǎn)程對(duì)象,向客戶端提供遠(yuǎn)程對(duì)象服務(wù) ?
package rmi2; import javax.naming.Context; import javax.naming.InitialContext;import rmi.RemoteInterface;public class ClientTest {public static void main(String args[]) {try {Context namingContext = new InitialContext();// 初始化命名內(nèi)容RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//獲得遠(yuǎn)程對(duì)象的存根對(duì)象System.out.println(RmObj2.doSomething());//通過(guò)遠(yuǎn)程對(duì)象,調(diào)用doSomething方法System.out.println("遠(yuǎn)程服務(wù)器計(jì)算結(jié)果為:" + RmObj2.Calculate(90, 2));} catch (Exception e) {}} }? ?在JDK1.3版本或更低的版本,如下方式注冊(cè);package rmi; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; //在JDK1.3版本或更低的版本,需要使用java.rmi.Naming來(lái)注冊(cè)遠(yuǎn)程對(duì)象 //創(chuàng)建RMI注冊(cè)表,啟動(dòng)RMI服務(wù),并將遠(yuǎn)程對(duì)象注冊(cè)到RMI注冊(cè)表中。 public class RMIServer {public static void main(String args[])throws java.rmi.AlreadyBoundException {try {// 創(chuàng)建一個(gè)遠(yuǎn)程對(duì)象RemoteObj,實(shí)質(zhì)上隱含了是生成stub和skeleton,并返回stub代理引用RemoteInterface remoteObj = new RemoteImpl();// 本地創(chuàng)建并啟動(dòng)RMI Service,被創(chuàng)建的Registry服務(wù)將在指定的端口,偵聽(tīng)請(qǐng)求// Java默認(rèn)端口是1099,缺少注冊(cè)表創(chuàng)建,則無(wú)法綁定對(duì)象到遠(yuǎn)程注冊(cè)表上LocateRegistry.createRegistry(8891);// 把遠(yuǎn)程對(duì)象注冊(cè)到RMI注冊(cè)服務(wù)器上,并命名為RemoteObj(名字可自定義,客戶端要對(duì)應(yīng))// 綁定的URL標(biāo)準(zhǔn)格式為:rmi://host:port/name(其中協(xié)議名可以省略,下面兩種寫法都是正確的)Naming.rebind("rmi://localhost:8891/RemoteObj", remoteObj);// 將stub代理綁定到Registry服務(wù)的URL上// Naming.bind("//localhost:8880/RemoteObj",remoteObj);System.out.println(">>>>>INFO:遠(yuǎn)程IHello對(duì)象綁定成功!");} catch (RemoteException e) {System.out.println("創(chuàng)建遠(yuǎn)程對(duì)象發(fā)生異常!");e.printStackTrace();} catch (MalformedURLException e) {System.out.println("發(fā)生URL畸形異常!");e.printStackTrace();}} }? ? ?RMIServer類主要實(shí)現(xiàn)注冊(cè)遠(yuǎn)程對(duì)象,并向客戶端提供遠(yuǎn)程對(duì)象服務(wù)。遠(yuǎn)程對(duì)象是在遠(yuǎn)程服務(wù)上創(chuàng)建的,你無(wú)法確切地知道遠(yuǎn)程服務(wù)器上的對(duì)象的名稱 。但是,將遠(yuǎn)程對(duì)象注冊(cè)到RMI Service之后,客戶端就可以通過(guò)RMI Service請(qǐng)求到該遠(yuǎn)程服務(wù)對(duì)象的stub了,利用stub代理就可以訪問(wèn)遠(yuǎn)程服務(wù)對(duì)象了 。
? ? ? 4. 客戶端代碼? ? ?Server端的代碼已經(jīng)全部寫完,這時(shí)把服務(wù)器的接口RemoteInterface打包成jar,以便在Client端的項(xiàng)目中使用。? ? ?項(xiàng)目-->右鍵-->導(dǎo)出-->jar->選擇RemoteInterface.java-->finish;關(guān)于客戶端如何導(dǎo)入jar包,請(qǐng)看這里:http://jingyan.baidu.com/article/ca41422fc76c4a1eae99ed9f.html
package rmi2; import javax.naming.Context; import javax.naming.InitialContext;import rmi.RemoteInterface; public class ClientTest {public static void main(String args[]) {try {Context namingContext = new InitialContext();// 初始化命名內(nèi)容RemoteInterface RmObj2 = (RemoteInterface) namingContext.lookup("rmi://localhost:8892/RemoteObj2");//獲得遠(yuǎn)程對(duì)象的存根對(duì)象System.out.println(RmObj2.doSomething());//通過(guò)遠(yuǎn)程對(duì)象,調(diào)用doSomething方法System.out.println("遠(yuǎn)程服務(wù)器計(jì)算結(jié)果為:" + RmObj2.Calculate(90, 2));} catch (Exception e) {}} }在JDK1.3版本或更低的版本,如下方式調(diào)用
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException;public class ClientTest {public static void main(String args[]) {try {// 在RMI服務(wù)注冊(cè)表中查找名稱為RemoteObj的對(duì)象,并調(diào)用其上的方法// 客戶端通過(guò)命名服務(wù)Naming獲得指向遠(yuǎn)程對(duì)象的遠(yuǎn)程引用RemoteInterface RmObj = (RemoteInterface) Naming.lookup("rmi://localhost:8881/RemoteObj");System.out.println(RmObj.doSomething());System.out.println("遠(yuǎn)程服務(wù)器計(jì)算結(jié)果為:" + RmObj.Calculate(1, 2));} catch (NotBoundException e) {e.printStackTrace();} catch (MalformedURLException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}} }輸出結(jié)果:OK ,You can do...... 遠(yuǎn)程服務(wù)器計(jì)算結(jié)果為:92例子代碼下載:http://download.csdn.net/detail/guyuealian/9583633
如果你覺(jué)得該帖子幫到你,還望貴人多多支持,鄙人會(huì)再接再厲,繼續(xù)努力的~
超強(qiáng)干貨來(lái)襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生
總結(jié)
以上是生活随笔為你收集整理的Java RMI远程方法调用详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BP神经网络的理解
- 下一篇: Java I/O流InputStream