Java RMI:Java遠端方法調用,即Java RMI(Java Remote Method Invocation)是Java程式語言裡,一種用於實現遠端過程調用的應用程式介面。
它使客戶機上運行的程式可以呼叫遠端伺服器上的物件。遠端方法呼叫特性使Java程式設計人員能夠在網路環境中分佈操作。 RMI全部的宗旨就是盡可能簡化遠端介面物件的使用。
我們知道遠端過程呼叫(Remote Procedure Call, RPC)可以用於一個進程呼叫另一個進程(很可能在另一個遠端主機上)中的過程,從而提供了過程的分佈能力。 Java 的 RMI 則在 RPC 的基礎上向前又邁進了一步,即提供分散式物件間的通訊。
RMI(Remote Method Invocation)為遠端方法調用,是允許運行在一個Java虛擬機的物件調用運行在另一個Java虛擬機器上的物件的方法。
這兩台虛擬機器可以是運作在相同電腦上的不同流程中,也可以是運作在網路上的不同電腦中。
【JavaRMI】
一、工作原理
RMI能讓一個Java程式去呼叫網路中另一台電腦的Java物件的方法,那麼呼叫的效果就像是在本機上呼叫一樣。通俗的講:A機器上面有一個class,透過遠端調用,B機器調用這個class 中的方法。
RMI,遠端方法呼叫(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分散式Java應用程式的方便途徑。 RMI是非常容易使用的,但它非常的強大。
RMI的基礎是接口,RMI架構基於一個重要的原理:定義接口和定義接口的具體實現是分開的。下面我們透過具體的例子,建立一個簡單的遠端運算服務和使用它的客戶程式
二、RMI包含部分:
1、遠端服務的接口定義
2、遠端服務介面的具體實作
3、樁(Stub)和框架(Skeleton)檔案
4、一個運行遠端服務的伺服器
5、一個RMI命名服務,它允許客戶端去發現這個遠端服務
6、類別檔案的提供者(一個HTTP或FTP伺服器)
7、一個需要這個遠端服務的客戶端程式
三、RMI的用途?
#RMI的用途是為分散式Java應用程式之間的遠端通訊提供服務,提供分佈式服務。
目前主要應用時封裝在各個J2EE專案框架中,例如Spring,EJB(Spring和EJB均封裝了RMI技術)
在Spring中實作RMI:
①在伺服器端定義服務的接口,定義特定的類別實作這些接口;
②在伺服器端使用org.springframework.remoting.rmi.RmiServiceExporter類別來註冊服務;
③在客戶端使用org.springframework.remoting.rmi.RmiProxyFactoryBean來實作遠端服務的代理功能;
④在客戶端定義存取與伺服器端服務介面相同的類別
# 、RMI的局限?
RMI目前使用Java遠端訊息交換協定JRMP(Java Remote Messaging Protocol)進行通訊。 JRMP是專為Java的遠端物件製定的協議,由於JRMP是專為Java物件製定的,因此,RMI對於使用非Java語言開發的應用系統的支援不足。
不能與用非Java語言書寫的物件通訊(意思是只支援客戶端和伺服器端都是Java程式的程式碼的遠端呼叫)。
五、RMI的使用限制?
由於客戶機和伺服器都是使用Java編寫的,二者平台相容性的要求只是雙方都運作在版本相容的Java虛擬機器上。
六、RMI呼叫遠端方法的參數和傳回值
當呼叫遠端物件上的方法時,客戶機除了可以將原始類型的資料作為參數一外,還可以將對像作為參數來傳遞,與之相對應的是返回值,可以返回原始類型或對象,這些都是透過Java的對象序列化(serialization)技術來實現的。 (換而言之:參數或傳回值如果是物件的話必須實作Serializable介面)
#七、 RMI應用程式的基本模型
######################### #######八、RMI體系結構###################樁/框架(Stub/Skeleton)層:客戶端的樁和伺服器端的框架;## #
遠端引用(remote reference)層:處理遠端引用行為
傳送層(transport):連線的建立與管理,以及遠端物件的追蹤
九、 RMI類別和介面(完成一個簡單RMI需要用到的類別)。
(一) Remote介面:是一個不定義方法的標記介面
Public interface Remote{}
#在RMI中,遠端介面宣告了可以從遠端Java虛擬機器呼叫的方法集。遠端介面符合下列要求:
1、遠端介面必須直接或間接擴充Java.rmi.Remote接口,且必須宣告為public,除非客戶端於遠端介面在同一包中
2.在遠端介面中的方法在宣告時,除了要拋出與應用程式有關的一場之外,還必須包括RemoteException(或它的超類,IOExcepion或Exception)異常
#3、在遠端方法聲明中,作為參數或傳回值聲明的遠端物件必須聲明為遠端接口,而非該介面的實作類別。
(二) RemoteObject抽象類別實作了Remote介面和序列化Serializable接口,它和它的子類別提供RMI伺服器函數。
(三) LocateRegistry final()類別用於取得特定主機的引導遠端物件註冊伺服器程式的參考(即建立stub),或建立能在特定連接埠接收呼叫的遠端物件註冊服務程式。
伺服器端:向其他客戶機提供遠端物件服務
SomeService servcie=……;//远程对象服务
1、Registry registry=LocateRegisty.getRegistry();//Registry是個接口,他繼承了Remote,此方法傳回本地主機在預設註冊表連接埠1099 上對遠端物件Registry 的參考。
2、getRegistry(int port) 傳回本機在指定port 上對遠端物件Registry 的參考;
3、getRegistry(String host) 傳回指定host 在預設登錄機埠1099 上遠端物件Registry 的參考;
4、getRegistry(String host, int port) 傳回指定的host 和port 上對遠端物件Registry 的參考
#5、registry.bind(「I serve”,service);// bind(String name,Remote obj) 綁定對此註冊表中指定name 的遠端參考。 name : 與此遠端參考相關的名稱 obj : 移除遠端物件(通常是一個 stub)的參考
6、unbind(String name)移除註冊表中指定name的綁定。
7、rebind(String name,Remote obj)重新綁定,如果name已存在,但是Remote不一樣則替換,如果Remote一樣則丟棄現有的綁定
#8、 lookup(String name) 傳回註冊表中綁定到指定name 的遠端引用,傳回Remote
9、String[] list() 傳回在此登錄中所綁定的名稱的陣列。該陣列將包含一個此註冊表中呼叫此方法時綁定的名稱快照。
客戶機端:向伺服器提供對應的服務請求。
Registry registry=LocateRegisty.getRegistry();
SomeService servcie=(SomeService)registry.lookup(“I serve”);
Servcie.requestService();
##(四) Naming類別和Registry類別類似。
客戶端:
Naming.lookup(String url) url 格式如下"rmi://localhost/"+远程对象引用
伺服器端:
Registry registry=LocateRegistry.createRegistry(int port); Naming.rebind(“service”,service);
(五) RMISecurityManager類別
在RMI引用程式中,如果沒有設定安全管理器,則只能從本機類別路徑載入stub和類,這可以確保應用程式不受遠端方法呼叫所下載的程式碼侵害
在從遠端主機下載程式碼之前必須執行下列程式碼來安裝RMISecurityManager:
System.setSecurityManager(new RMISecurityManager());
#第十、demo開發###為了寫一個demo,我們分成兩個部分,一部分是server端的程式碼,一部分是client端的程式碼,client端的程式碼主要是為了使用server端的程式碼。當然這個程式碼是非常簡單的,只是為了說明問題,現實的使用會讓比較複雜的。 ######(一) 我們的目的######建立一個server端的java project,包含遠端的程式碼,定義接口,定義接口實現,然後在建立一個client端的java project,透過RMI使用遠端服務中的方法。 ######(二) 我們的程式碼結構###############(三) 遠端服務代碼######1. 遠端服務的介面定義### ###第一步就是建立和編譯服務介面的Java程式碼。這個介面定義了所有的提供遠端服務的功能,以下是原始程式:######UserManagerInterface.java###package cn.com.tt.rmiserver.stub; import java.rmi.Remote; import java.rmi.RemoteException; import cn.com.tt.rmiserver.bean.Account; public interface UserManagerInterface extends Remote{ public String getUserName() throws RemoteException; public Account getAdminAccount() throws RemoteException; }
package cn.com.tt.rmiserver; import java.rmi.RemoteException; import cn.com.tt.rmiserver.stub.UserManagerInterface; import cn.com.tt.rmiserver.bean.Account; public class UserManagerImp implements UserManagerInterface { public UserManagerImp() throws RemoteException { } private static final long serialVersionUID = -3111492742628447261L; public String getUserName() throws RemoteException{ return "TT"; } public Account getAdminAccount() throws RemoteException{ Account account=new Account(); account.setUsername("TT"); account.setPassword("123456"); return account; } }
package cn.com.tt.rmiserver.bean; import java.io.Serializable; public class Account implements Serializable,Cloneable{ private static final long serialVersionUID = -1858518369668584532L; private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package cn.com.tt.rmiserver.entry; import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import cn.com.tt.rmiserver.UserManagerImp; import cn.com.tt.rmiserver.stub.UserManagerInterface; public class Entry { public static void main(String []args) throws AlreadyBoundException, RemoteException{ UserManagerImp userManager=new UserManagerImp(); UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0); // Bind the remote object's stub in the registry Registry registry = LocateRegistry.createRegistry(2002); registry.rebind("userManager", userManagerI); System.out.println("server is ready"); } }
1、把Server端的Account类和接口UserManagerInterface 导出Export成jar包,命名为:RmiServerInterface.jar。导入到client中。
2、项目——右键——Export——java——jar file——next——选择Account类和接口UserManagerInterface——命名为:RmiServerInterface.jar如下图:
3. 新建一个java Project,导入jar包,编写客户端代码。
4. 代码
ClientEntry.java
package weiblog.rmi; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import cn.com.tt.rmiserver.stub.UserManagerInterface; public class ClientEntry { public static void main(String []args){ try { Registry registry = LocateRegistry.getRegistry("localhost",2004); UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager"); System.out.println("用户名是"+userManager.getAdminAccount().getUsername() +"密码"+userManager.getAdminAccount().getPassword()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NotBoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
5. 先运行服务器端代码, 然后运行客户端代码,就会显示运行结果,客户端可以运行多次,每次都可以取得服务器端的对象。如果要再次运行客户端代码就需要更改端口号,如果不更改就会显示端口号被占用。
更多java知识请关注java基础教程栏目。
以上是Java RMI圖文詳解(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!