明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

漫談EJB (2)

[摘要]EJB技術(shù)的基礎(chǔ)是另外兩種技術(shù):RMI-IIOP和JNDI。要想了解EJB,一定要先了解RMI-IIOP和JNDI。因此,我們在介紹EJB細節(jié)之前,先了解這兩項技術(shù)。我們的介紹比較基本,因此大多數(shù)組織只要了解這些就已經(jīng)夠了。Java RMI-IIOP Java RMI-IIOP(Java R...
EJB技術(shù)的基礎(chǔ)是另外兩種技術(shù):RMI-IIOP和JNDI。要想了解EJB,一定要先了解RMI-IIOP和JNDI。因此,我們在介紹EJB細節(jié)之前,先了解這兩項技術(shù)。我們的介紹比較基本,因此大多數(shù)組織只要了解這些就已經(jīng)夠了。

Java RMI-IIOP

Java RMI-IIOP(Java Remote Method Invocation over the Internet Inter-ORB Protocol)是J2EE的網(wǎng)絡(luò)機制。Java RMI-IIOP允許你編寫分布式對象,使得對象的通信范圍能夠在內(nèi)存中,跨Java虛擬機,跨物理設(shè)備。

Remote Method Invocation

RPC(remote procedure call)是一臺機器的進程調(diào)用另一臺機器的進程的過程。而remote method invocation則比RPC的概念更進一步,允許分布式對象間的通信。RMI-IIOP允許調(diào)用遠程對象的方法,而不僅僅是過程。這有利于面向?qū)ο缶幊獭ava RMI (Remote Method Invocation 遠程方法調(diào)用)是用Java在JDK1.1中實現(xiàn)的,它大大增強了Java開發(fā)分布式應用的能力。Java作為一種風靡一時的網(wǎng)絡(luò)開發(fā)語言,其巨大的威力就體現(xiàn)在它強大的開發(fā)分布式網(wǎng)絡(luò)應用的能力上,而RMI就是開發(fā)百分之百純Java的網(wǎng)絡(luò)分布式應用系統(tǒng)的核心解決方案之一。其實它可以被看作是RPC的Java版本。但是傳統(tǒng)RPC并不能很好地應用于分布式對象系統(tǒng)。而Java RMI 則支持存儲于不同地址空間的程序級對象之間彼此進行通信,實現(xiàn)遠程對象之間的無縫遠程調(diào)用。

remote method invocation決不簡單,需要考慮幾個問題:

marshalling和unmarshalling.在不同機器間通過網(wǎng)絡(luò)傳遞變量(包括Java基本類型和對象),如果目標機器表示數(shù)據(jù)的方式和原機器不同該怎么辦?例如二進制庫不同。因此marshalling和unmarshalling就是傳遞變量的過程。

變量傳遞方法.變量有兩種傳遞方法:pass-by-value和pass-by-reference。對于前者,你的目標方法只需使用一份copy,但對于后者,遠程方法對變量的任何修改都會影響到源數(shù)據(jù)。

網(wǎng)絡(luò)和機器的不穩(wěn)定.需要有一種機制保證一個JVM崩潰之后,不會影響系統(tǒng)的正常運作。

在 Java 分布式對象模型中,remote object 是這樣一種對象:它的方法可以從其它 Java 虛擬機(可能在不同的主機上)中調(diào)用。該類型的對象由一種或多種 remote interfaces(它是聲明遠程對象方法的 Java 接口)描述。遠程方法調(diào)用 (RMI) 就是調(diào)用遠程對象上遠程接口的方法的動作。更為重要的是,遠程對象的方法調(diào)用與本地對象的方法調(diào)用語法相同。

Remote Interface

RMI-IIOP遵循了接口和實現(xiàn)的原則。你寫的所有網(wǎng)絡(luò)代碼都是應用于接口,而不是實現(xiàn)。實際上,你必須使用RMI-IIOP中的范例,沒有其它的選擇。直接在你的對象實現(xiàn)上執(zhí)行遠程調(diào)用是不可能的,你只能在對象類的接口上單獨進行這一操作。

所以我們在使用RMI-IIOP時,你必須建立一個客戶接口,叫做remote interface。這個遠程接口應該擴展java.rmi.Remote接口。

Remote Object Implementation

遠程對象和客戶機的物理位置并不是很重要。可以運行在同一地址空間或是跨Internet運行。

為了使對象成為一個遠程對象,你需要執(zhí)行一下步驟:

繼承javax.rmi.PortableRemoteObject。PortableRemoteObject是進行遠程調(diào)用的基類,當你的遠程對象調(diào)用構(gòu)造器時,PortableRemoteObject對象的構(gòu)造器也會自動被調(diào)用。

不繼承javax.rmi.PortableRemoteObject。如果你的遠程對象需要繼承其它的類,而Java不允許多重繼承,因此你不能繼承PortableRemoteObject。這時,你需要手動調(diào)用javax.rmi.PortableRemoteObject.exportObject()。

Stub和Skeletons

我們來看看在RMI-IIOP背后隱藏的網(wǎng)絡(luò)架構(gòu)。RMI-IIOP的一個好處就是你可以不用管你要調(diào)用的對象是本地的還是遠程的。這就叫做local/remote transparency。

RMI應用程序通常包括兩個獨立的程序:服務器程序和客戶機程序。典型的服務器應用程序?qū)?chuàng)建多個遠程對象,使這些遠程對象能夠被引用,然后等待客戶機調(diào)用這些遠程對象的方法。而典型的客戶機程序則從服務器中得到一個或多個遠程對象的引用,然后調(diào)用遠程對象的方法。RMI為服務器和客戶機進行通信和信息傳遞提供了一種機制。






在與遠程對象的通信過程中,RMI使用標準機制:stub和skeleton。遠程對象的stub擔當遠程對象的客戶本地代表或代理人角色。調(diào)用程序?qū)⒄{(diào)用本地stub的方法,而本地stub將負責執(zhí)行對遠程對象的方法調(diào)用。在RMI中,遠程對象的stub與該遠程對象所實現(xiàn)的遠程接口集相同。調(diào)用stub的方法時將執(zhí)行下列操作:(1) 初始化與包含遠程對象的遠程虛擬機的連接;(2) 對遠程虛擬機的參數(shù)進行編組(寫入并傳輸);(3) 等待方法調(diào)用結(jié)果;(4) 解編(讀。┓祷刂祷蚍祷氐漠惓#(5) 將值返回給調(diào)用程序。為了向調(diào)用程序展示比較簡單的調(diào)用機制,stub將參數(shù)的序列化和網(wǎng)絡(luò)級通信等細節(jié)隱藏了起來。在遠程虛擬機中,每個遠程對象都可以有相應的skeleton(在JDK1.2環(huán)境中無需使用skeleton)。Skeleton負責將調(diào)用分配給實際的遠程對象實現(xiàn)。它在接收方法調(diào)用時執(zhí)行下列操作:(1) 解編(讀取)遠程方法的參數(shù);(2) 調(diào)用實際遠程對象實現(xiàn)上的方法;(3) 將結(jié)果(返回值或異常)編組(寫入并傳輸)給調(diào)用程序。stub和skeleton由rmic編譯器生成。

要實現(xiàn)local/remote transparency可沒有那么簡單。為了屏蔽你調(diào)用的是遠端主機上的對象,RMI-IIOP需要模擬一個本地對象供你調(diào)用。這個本地對象叫做stub。它負責接受本地的方法調(diào)用請求,把這些請求委托給真正實現(xiàn)它們的對象(可以通過網(wǎng)絡(luò)定位)。這樣就使得遠程調(diào)用看起來就和本地調(diào)用一樣。

利用RMI編寫分布式對象應用程序需要完成以下工作:(1) 定位遠程對象。應用程序可使用兩種機制中的一種得到對遠程對象的引用。它既可用RMI的簡單命名工具rmiregistry來注冊它的遠程對象,也可以將遠程對象引用作為常規(guī)操作的一部分來進行傳遞和返回。(2)與遠程對象通信。遠程對象間通信的細節(jié)由RMI處理,對于程序員來說,遠程通信看起來就像標準的Java方法調(diào)用。(3)給作為參數(shù)或返回值傳遞的對象加載類字節(jié)碼。因為RMI允許調(diào)用程序?qū)⒓僇ava對象傳給遠程對象,所以,RMI將提供必要的機制,既可以加載對象的代碼又可以傳輸對象的數(shù)據(jù)。在RMI分布式應用程序運行時,服務器調(diào)用注冊服務程序以使名字與遠程對象相關(guān)聯(lián)?蛻魴C在服務器上的注冊服務程序中用遠程對象的名字查找該遠程對象,然后調(diào)用它的方法。

定位遠程對象。應用程序可使用兩種機制中的一種得到對遠程對象的引用。它既可用 RMI 的簡單命名工具 rmiregistry 來注冊它的遠程對象;也可將遠程對象引用作為常規(guī)操作的一部分來進行傳遞和返回。

與遠程對象通訊。遠程對象間通訊的細節(jié)由 RMI 處理;對于程序員來說,遠程通訊看起來就象標準的 Java 方法調(diào)用。給作為參數(shù)或返回值傳遞的對象加載類字節(jié)碼因為 RMI允許調(diào)用程序?qū)⒓?Java 對象傳給遠程對象,所以 RMI 將提供必要的機制,既可以加載對象的代碼又可以傳輸對象的數(shù)據(jù)。服務器調(diào)用注冊服務程序以使名字與遠程對象相關(guān)聯(lián)?蛻魴C在服務器注冊服務程序中用遠程對象的名字查找該遠程對象,然后調(diào)用它的方法。RMI 能用 Java系統(tǒng)支持的任何 URL 協(xié)議(例如 HTTP、FTP、file 等)加載類字節(jié)碼。

stub只是解決了一半的問題。我們還希望遠程對象也不用考慮網(wǎng)絡(luò)問題。因此遠程對象也需要一個本地的skeleton來接受調(diào)用。skeleton接受網(wǎng)絡(luò)調(diào)用并把調(diào)用委托給遠程對象實現(xiàn)。

你的J2EE服務器應當提供一種方法來產(chǎn)生必須的stub和skeleton,以減輕你的對網(wǎng)絡(luò)問題考慮的負擔。典型的是通過命令行工具來完成,例如sun的J2EE參考實現(xiàn)包就使用了一個名為rmic(RMI compiler)的工具來產(chǎn)生stub和skeleton類。你應當把stub部署在客戶機上,并把skeleton部署在服務器上。

對象序列化和變量傳遞

在RMI分布式應用系統(tǒng)中,服務器與客戶機之間傳遞的Java對象必須是可序列化的對象。不可序列化的對象不能在對象流中進行傳遞。對象序列化擴展了核心Java輸入/輸出類,同時也支持對象。對象序列化支持把對象編碼以及將通過它們可訪問到的對象編碼變成字節(jié)流;同時,它也支持流中對象圖形的互補重構(gòu)造。序列化用于輕型持久性和借助于套接字或遠程方法調(diào)用(RMI)進行的通信。序列化中現(xiàn)在包括一個 API(Application Programming Interface,應用程序接口),允許獨立于類的域指定對象的序列化數(shù)據(jù),并允許使用現(xiàn)有協(xié)議將序列化數(shù)據(jù)域?qū)懭肓髦谢驈牧髦凶x取,以確保與缺省讀寫機制的兼容性。

為編寫應用程序,除多數(shù)瞬態(tài)應用程序外,都必須具備存儲和檢索 Java對象的能力。以序列化方式存儲和檢索對象的關(guān)鍵在于提供重新構(gòu)造該對象所需的足夠?qū)ο鬆顟B(tài)。存儲到流的對象可能會支持 Serializable(可序列化)或 Externalizable(可外部化)接口。對于Java對象,序列化形式必須能標識和校驗存儲其內(nèi)容的對象所屬的 Java類,并且將該內(nèi)容還原為新的實例。對于可序列化對象,流將提供足夠的信息將流的域還原為類的兼容版本。對于可外部化對象,類將全權(quán)負責其內(nèi)容的外部格式。序列化 Java 對象的目的是:提供一種簡單但可擴充的機制,以序列化方式維護 Java對象的類型及安全屬性;具有支持編組和解編的擴展能力以滿足遠程對象的需要;具有可擴展性以支持 Java 對象的簡單持久性;只有在自定義時,才需對每個類提供序列化自實現(xiàn);允許對象定義其外部格式。

java.rmi.Remote 接口

在 RMI 中,遠程接口是聲明了可從遠程 Java 虛擬機中調(diào)用的方法集。遠程接
口必須滿足下列要求:

遠程接口至少必須直接或間接擴展 java.rmi.Remote 接口。
遠程接口中的方法聲明必須滿足下列遠程方法聲明的要求:
遠程方法聲明在其 throws 子句中除了要包含與應用程序有關(guān)的異常(注意與應用程序有關(guān)的異常無需擴展 java.rmi.RemoteException )之外,還必須包括 java.rmi.RemoteException 異常(或它的超類,例如java.io.IOException 或 java.lang.Exception )。
遠程方法聲明中,作為參數(shù)或返回值聲明的(在參數(shù)表中直接聲明或嵌入到參數(shù)的非遠程對象中)遠程對象必須聲明為遠程接口,而非該接口的實現(xiàn)類。

java.rmi.Remote 接口是一個不定義方法的標記接口:

public interface Remote

遠程接口必須至少擴展 java.rmi.Remote 接口(或其它擴展java.rmi.Remote 的遠程接口)。然而,遠程接口在下列情況中可以擴展非遠程接口:

遠程接口也可擴展其它非遠程接口,只要被擴展接口的所有方法(如果有)滿足遠程方法聲明的要求。
例如,下面的接口 BankAccount 即為訪問銀行帳戶定義了一個遠程接口。它包含往帳戶存款、使帳戶收支平衡和從帳戶取款的遠程方法:

public interface BankAccount extends java.rmi.Remote
{
public void deposit(float amount)
throws java.rmi.RemoteException;
public void withdraw(float amount)
throws OverdrawnException, java.rmi.RemoteException;
public float getBalance()
throws java.rmi.RemoteException;
}

下例說明了有效的遠程接口 Beta。它擴展非遠程接口 Alpha(有遠程方法)和接口 java.rmi.Remote:
public interface Alpha
{
public final String okay = "constants are okay too";
public Object foo(Object obj)
throws java.rmi.RemoteException;
public void bar() throws java.io.IOException;
public int baz() throws java.lang.Exception;
}

public interface Beta extends Alpha, java.rmi.Remote {
public void ping() throws java.rmi.RemoteException;
}

RemoteException 類

java.rmi.RemoteException 類是在遠程方法調(diào)用期間由 RMI 運行時所拋出的異常的超類。為確保使用 RMI 系統(tǒng)的應用程序的健壯性,遠程接口中聲明的遠程方法在其 throws 子句中必須指定 java.rmi.RemoteException(或它的超類,例如 java.io.IOException 或 java.lang.Exception)。

當遠程方法調(diào)用由于某種原因失敗時,將拋出 java.rmi.RemoteException 異常。遠程方法調(diào)用失敗的原因包括:

通訊失。ㄟh程服務器不可達或拒絕連接;連接被服務器關(guān)閉等。)
參數(shù)或返回值傳輸或讀取時失敗
協(xié)議錯誤

RemoteException 類是一個已檢驗的異常(必須由遠程方法的調(diào)用程序處理并經(jīng)編譯器檢驗的異常),而不是 RuntimeException。

RemoteObject 類及其子類

RMI 服務器函數(shù)由 java.rmi.server.RemoteObject 及其子類java.rmi.server.RemoteServer、java.rmi.server.UnicastRemoteObject和 java.rmi.activation.Activatable 提供。

java.rmi.server.RemoteObject 為對遠程對象敏感的 java.lang.Object方法、hashCode、 equals 和 toString 提供實現(xiàn)。

創(chuàng)建遠程對象并將其導出(使它們可為遠程客戶機利用)所需的方法由類UnicastRemoteObject 和 Activatable 提供。子類可以識別遠程引用的語義,例如服務器是簡單的遠程對象還是可激活的遠程對象(調(diào)用時將執(zhí)行的遠程對象)。java.rmi.server.UnicastRemoteObject 類定義了單體(單路傳送)遠程對
象,其引用只有在服務器進程活著時才有效。類 java.rmi.activation.Activatable 是抽象類,它定義的 activatable遠程對象在其遠程方法被調(diào)用時開始執(zhí)行并在必要時自己關(guān)閉。

實現(xiàn)遠程接口

實現(xiàn)遠程接口的類的一般規(guī)則如下:

該類通常擴展 java.rmi.server.UnicastRemoteObject,因而將繼承類java.rmi.server.RemoteObject 和java.rmi.server.RemoteServer 提供的遠程行為。
該類能實現(xiàn)任意多的遠程接口。
該類能擴展其它遠程實現(xiàn)類。
該類能定義遠程接口中不出現(xiàn)的方法,但這些方法只能在本地使用而不能在遠程使用。

例如,下面的類 BankAcctImpl 實現(xiàn) BankAccount 遠程接口并擴展java.rmi.server.UnicastRemoteObject 類:

package mypackage;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class BankAccountImpl extends UnicastRemoteObject implements
BankAccount

{
private float balance = 0.0;

public BankAccountImpl(float initialBalance)
throws RemoteException
{
balance = initialBalance;
}

public void deposit(float amount) throws RemoteException
{
...
}

public void withdraw(float amount) throws OverdrawnException,
RemoteException
{
...
}

public float getBalance() throws RemoteException
{
...
}
}


注意:必要時,實現(xiàn)遠程接口的類能擴展除java.rmi.server.UnicastRemoteObject 類以外的其它一些類。但實現(xiàn)類此時必須承擔起一定的責任,即導出對象(由 UnicastRemoteObject 構(gòu)造函數(shù)負責)和實現(xiàn)從 java.lang.Object 類繼承的 hashCode、 equals 和toString 方法的正確遠程語義(如果需要)。


遠程方法調(diào)用中的參數(shù)傳遞

傳給遠程對象的參數(shù)或源于它的返回值可以是任意可序列化的 Java 對象。這包括 Java 基本類型, 遠程?Java 對象和實現(xiàn) java.io.Serializable 接口的非遠程 Java 對象。有關(guān)如何使類序列化的詳細信息,參見 Java“對象序列化規(guī)范”。本地得不到的作為參數(shù)或返回值的類,可通過 RMI 系統(tǒng)進行動態(tài)下載。

傳遞非遠程對象

非遠程對象將作為遠程方法調(diào)用的參數(shù)傳遞或作為遠程方法調(diào)用的結(jié)果返回時,是通過復制傳遞的;也就是使用 Java 對象序列化機制將該對象序列化。因此,在遠程對象調(diào)用過程中,當非遠程對象作為參數(shù)或返回值傳遞時,非遠程對象的內(nèi)容在調(diào)用遠程對象之前將被復制。從遠程方法調(diào)用返回非遠程對象時,將在調(diào)用的虛擬機中創(chuàng)建新對象。

傳遞遠程對象

當將遠程對象作為遠程方法調(diào)用的參數(shù)或返回值傳遞時,遠程對象的 stub 程序即被傳遞出去。作為參數(shù)傳遞的遠程對象僅能實現(xiàn)遠程接口。

引用的完整性

如果一個對象的兩個引用在單個遠程方法調(diào)用中以參數(shù)形式(或返回值形式)從一個虛擬機傳到另一個虛擬機中,并且它們在發(fā)送虛擬機中指向同一對象,則兩個引用在接收虛擬機中將指向該對象的同一副本。進一步說就是:在單個遠程方法調(diào)用中,RMI 系統(tǒng)將在作為調(diào)用參數(shù)或返回值傳遞的對象中保持引用的完整性。

類注解

當對象在遠程調(diào)用中被從一個虛擬機發(fā)送到另一個虛擬機中時,RMI 系統(tǒng)在調(diào)用流中用類的信息 (URL) 給類描述符加注解,以便該類能在接收器上加載。在遠程方法調(diào)用期間,調(diào)用可隨時下載類。

參數(shù)傳輸

為將 RMI 調(diào)用的參數(shù)序列化到遠程調(diào)用的目的文件里,需要將該參數(shù)寫入作為java.io.ObjectOutputStream 類的子類的流中。ObjectOutputStream 子類將覆蓋 replaceObject 方法,目的是用其相應的 stub 類取代每個遠程對象。對象參數(shù)將通過 ObjectOutputStream 的 writeObject 方法寫入流中。而ObjectOutputStream 則通過 writeObject 方法為每個寫入流中的對象(包含所寫對象所引用的對象)調(diào)用 replaceObject 方法。RMIObjectOutputStream子類的 replaceObject 方法返回下列值:
如果傳給 replaceObject 的對象是 java.rmi.Remote 的實例,則返回遠程對象的 stub 程序。遠程對象的 stub 程序通過對java.rmi.server.RemoteObject.toStub方法的調(diào)用而獲得。如果傳給 replaceObject 的對象不是 java.rmi.Remote 的實例,則只返回該對象。

RMI 的 ObjectOutputStream 子類也實現(xiàn) annotateClass 方法,該方法用類的位置注解調(diào)用流以便能在接收器中下載該類。有關(guān)如何使用 annotateClass的詳細信息,參見“動態(tài)類加載”一節(jié)。因為參數(shù)只寫入一個 ObjectOutputStream,所以指向調(diào)用程序同一對象的引用將在接收器那里指向該對象的同一副本。在接收器上,參數(shù)將被單個ObjectInputStream 所讀取。

用于寫對象的 ObjectOutputStream(類似的還有用于讀對象的ObjectInputStream )的所有其它缺省行為將保留在參數(shù)傳遞中。例如,寫對象時對 writeReplace 的調(diào)用及讀對象時對 readResolve 的調(diào)用就是由 RMI的參數(shù)編組與解編流完成的。

與上述 RMI 參數(shù)傳遞方式類似,返回值(或異常)將被寫入ObjectOutputStream的子類并和參數(shù)傳輸?shù)奶娲袨橄嗤?

定位遠程對象

我們專門提供了一種簡單的引導名字服務器,用于存儲對遠程對象的已命名引用。使用類 java.rmi.Naming 的基于 URL 的方法可以存儲遠程對象引用。客戶機要調(diào)用遠程對象的方法,則必須首先得到該對象的引用。對遠程對象的引用通常是在方法調(diào)用中以返回值的形式取得。RMI 系統(tǒng)提供一種簡單的引導名字服務器,通過它得到給定主機上的遠程對象。java.rmi.Naming 類提供基于統(tǒng)一資源定位符 (URL) 的方法,用來綁定、再綁定、解開和列出位于某一主機及端口上的名字-對象對。


標簽:漫談EJB (2)