程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 漫談EJB在Java中的應用(二)

漫談EJB在Java中的應用(二)

編輯:關於JAVA

EJB技術的基礎是另外兩種技術:RMI-IIOP和JNDI.

要想了解EJB,一定要先了解RMI-IIOP和JNDI.因此,我們在介紹EJB細節之前,先了解這兩項技術。我們的介紹比較基本,因此大多數組織只要了解這些就已經夠了。

Java RMI-IIOP

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

Remote Method Invocation

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

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

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

變量傳遞方法。變量有兩種傳遞方法:pass-by-value和pass-by-reference.

對於前者,你的目標方法只需使用一份copy,但對於後者,遠程方法對變量的任何修改都會影響到源數據。

網絡和機器的不穩定。需要有一種機制保證一個JVM崩潰之後,不會影響系統的正常運作。

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

Remote Interface

RMI-IIOP遵循了接口和實現的原則。你寫的所有網絡代碼都是應用於接口,而不是實現。實際上,你必須使用RMI-IIOP中的范例,沒有其它的選擇。直接在你的對象實現上執行遠程調用是不可能的,你只能在對象類的接口上單獨進行這一操作。

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

Remote Object Implementation

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

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

繼承Javax.rmi.PortableRemoteObject.PortableRemoteObject是進行遠程調用的基類,當你的遠程對象調用構造器時,PortableRemoteObject對象的構造器也會自動被調用。

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

Stub和Skeletons

我們來看看在RMI-IIOP背後隱藏的網絡架構。RMI-IIOP的一個好處就是你可以不用管你要調用的對象是本地的還是遠程的。這就叫做local/remote transparency.

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

在與遠程對象的通信過程中,RMI使用標准機制:stub和skeleton.遠程對象的stub擔當遠程對象的客戶本地代表或代理人角色。

調用程序將調用本地stub的方法,而本地stub將負責執行對遠程對象的方法調用。

在RMI中,遠程對象的stub與該遠程對象所實現的遠程接口集相同。

調用stub的方法時將執行下列操作:

(1) 初始化與包含遠程對象的遠程虛擬機的連接;

(2) 對遠程虛擬機的參數進行編組(寫入並傳輸);

(3) 等待方法調用結果;

(4) 解編(讀取)返回值或返回的異常;

(5) 將值返回給調用程序。

為了向調用程序展示比較簡單的調用機制,stub將參數的序列化和網絡級通信等細節隱藏了起來。在遠程虛擬機中,每個遠程對象都可以有相應的skeleton(在JDK1.2環境中無需使用skeleton)。

Skeleton負責將調用分配給實際的遠程對象實現。

它在接收方法調用時執行下列操作:

(1) 解編(讀取)遠程方法的參數;

(2) 調用實際遠程對象實現上的方法;

(3) 將結果(返回值或異常)編組(寫入並傳輸)給調用程序。

stub和skeleton由rmic編譯器生成。

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

利用RMI編寫分布式對象應用程序需要完成以下工作:

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

(2)與遠程對象通信。遠程對象間通信的細節由RMI處理,對於程序員來說,遠程通信看起來就像標准的Java方法調用。

(3)給作為參數或返回值傳遞的對象加載類字節碼。因為RMI允許調用程序將純Java對象傳給遠程對象,所以,RMI將提供必要的機制,既可以加載對象的代碼又可以傳輸對象的數據。

在RMI分布式應用程序運行時,服務器調用注冊服務程序以使名字與遠程對象相關聯。客戶機在服務器上的注冊服務程序中用遠程對象的名字查找該遠程對象,然後調用它的方法。

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

與遠程對象通訊。遠程對象間通訊的細節由 RMI 處理;對於程序員來說,遠程通訊看起來就象標准的 Java 方法調用。給作為參數或返回值傳遞的對象加載類字節碼因為 RMI允許調用程序將純 Java 對象傳給遠程對象,所以 RMI 將提供必要的機制,既可以加載對象的代碼又可以傳輸對象的數據。服務器調用注冊服務程序以使名字與遠程對象相關聯。客戶機在服務器注冊服務程序中用遠程對象的名字查找該遠程對象,然後調用它的方法。RMI 能用 Java系統支持的任何 URL 協議(例如 HTTP、FTP、file 等)加載類字節碼。

stub只是解決了一半的問題。我們還希望遠程對象也不用考慮網絡問題。因此遠程對象也需要一個本地的skeleton來接受調用。skeleton接受網絡調用並把調用委托給遠程對象實現。

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

對象序列化和變量傳遞

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

為編寫應用程序,除多數瞬態應用程序外,都必須具備存儲和檢索 Java對象的能力。以序列化方式存儲和檢索對象的關鍵在於提供重新構造該對象所需的足夠對象狀態。

存儲到流的對象可能會支持 Serializable(可序列化)或 Externalizable(可外部化)接口。

對於Java對象,序列化形式必須能標識和校驗存儲其內容的對象所屬的 Java類,並且將該內容還原為新的實例。

對於可序列化對象,流將提供足夠的信息將流的域還原為類的兼容版本。

對於可外部化對象,類將全權負責其內容的外部格式。

序列化 Java 對象的目的是:

提供一種簡單但可擴充的機制,以序列化方式維護 Java對象的類型及安全屬性;具有支持編組和解編的擴展能力以滿足遠程對象的需要;具有可擴展性以支持 Java 對象的簡單持久性;只有在自定義時,才需對每個類提供序列化自實現;允許對象定義其外部格式。

Java.rmi.Remote 接口

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

遠程接口至少必須直接或間接擴展 Java.rmi.Remote 接口。

遠程接口中的方法聲明必須滿足下列遠程方法聲明的要求:遠程方法聲明在其 throws 子句中除了要包含與應用程序有關的異常(注意與應用程序有關的異常無需擴展 java.rmi.RemoteException )之外,還必須包括 java.rmi.RemoteException 異常(或它的超類,例如java.io.IOException 或 Java.lang.Exception )。

遠程方法聲明中,作為參數或返回值聲明的(在參數表中直接聲明或嵌入到參數的非遠程對象中)遠程對象必須聲明為遠程接口,而非該接口的實現類。

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

當遠程方法調用由於某種原因失敗時,將拋出 Java.rmi.RemoteException 異常。遠程方法調用失敗的原因包括:

通訊失敗(遠程服務器不可達或拒絕連接;連接被服務器關閉等。)

參數或返回值傳輸或讀取時失敗協議錯誤

RemoteException 類是一個已檢驗的異常(必須由遠程方法的調用程序處理並經編譯器檢驗的異常),而不是 RuntimeException.

RemoteObject 類及其子類

RMI 服務器函數由 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 提供實現。

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

實現遠程接口

實現遠程接口的類的一般規則如下:

該類通常擴展 java.rmi.server.UnicastRemoteObject,因而將繼承類java.rmi.server.RemoteObject 和Java.rmi.server.RemoteServer 提供的遠程行為。

該類能實現任意多的遠程接口。

該類能擴展其它遠程實現類。

該類能定義遠程接口中不出現的方法,但這些方法只能在本地使用而不能在遠程使用。

例如,下面的類 BankAcctImpl 實現 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

{

...

}

}

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

遠程方法調用中的參數傳遞

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

傳遞非遠程對象

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

傳遞遠程對象

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

引用的完整性

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

類注解

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

參數傳輸

為將 RMI 調用的參數序列化到遠程調用的目的文件裡,需要將該參數寫入作為Java.io.ObjectOutputStream 類的子類的流中。

ObjectOutputStream 子類將覆蓋 replaceObject 方法,目的是用其相應的 stub 類取代每個遠程對象。

對象參數將通過 ObjectOutputStream 的 writeObject 方法寫入流中。

而ObjectOutputStream 則通過 writeObject 方法為每個寫入流中的對象(包含所寫對象所引用的對象)調用 replaceObject 方法。

RMIObjectOutputStream子類的 replaceObject 方法返回下列值:

如果傳給 replaceObject 的對象是 java.rmi.Remote 的實例,則返回遠程對象的 stub 程序。遠程對象的 stub 程序通過對Java.rmi.server.RemoteObject.toStub方法的調用而獲得。

如果傳給 replaceObject 的對象不是 Java.rmi.Remote 的實例,則只返回該對象。

RMI 的 ObjectOutputStream 子類也實現 annotateClass 方法,該方法用類的位置注解調用流以便能在接收器中下載該類。

有關如何使用 annotateClass的詳細信息,參見“動態類加載”一節。

因為參數只寫入一個 ObjectOutputStream,所以指向調用程序同一對象的引用將在接收器那裡指向該對象的同一副本。在接收器上,參數將被單個ObjectInputStream 所讀取。

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

與上述 RMI 參數傳遞方式類似,返回值(或異常)將被寫入ObjectOutputStream的子類並和參數傳輸的替代行為相同。

定位遠程對象

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved