構建一個EJB組件的步驟如下: 1.編寫組件接口(EJBObject遠程接口,EJBLocalObject本地接口),Home接口,Enterprise Bean等。 2.編寫部署描述文件。ejb-jar.xml等。 3.利用Jar工具將以上打包爲Ejb-jar文件。如:jar cvfm HelloWorld.jar * 4.發佈EJB。
EJB對象: 1.由容器生成的。 2.實現了遠程接口或本地接口。 3.客戶端通過EJB對象委託調用enterpriseBean的方法。
遠程接口: 1.繼承javax.ejb.EJBObject。 2.定義並公開enterpriseBean的方法。 3.客戶端通過該接口與容器生成的EJB對象交互。 4.其中定義的方法都必須拋出java.rmi.RemoteException異常。這是必須的,因爲它繼了EJBObject,而EJBObject實現了Remote接口。 5.客戶端通過遠程接口調用enterpriseBean的效率是很底的。原因如下: (1)首先客戶端需調用本地Stub。 (2)Stub將參數格式化爲適合網絡傳輸的格式。 (3)Stub與服務器端的Skeleton連接。 (4)Skeleton解釋被Stub格式化了的參數。 (5)Skeleton調用EJB對象。 (6)EJB對象連接緩衝,事務處理,安全檢查,生命週期服務等工作。 (7)調用enterpriseBean的業務方法。當enterpriseBean完成任務,返回Home時,將重得執行以上步驟。
本地接口: 1.繼承javax.ejb.EJBLocalObject。 2.本地用戶可以通過本地接口高性能的訪問EJB對象。(免去執行遠程接口中的第5項工作)
Home接口: 1.繼承javax.ejb.EJBHome。 2.用來創建EJB對象。 3.其中有個Create方法返回容器實現了遠程接口的EJB對象。並拋出兩個異常。java.Rmi.RemoteException和javax.ejb.CreateException。
本地Home接口: 1.繼承javax.ejb.EJBLocalHome。 2.同本地接口相同,它是本地客戶端可以使用的高性能Home接口。 3.其中有個Create方法返回容器實現了本地接口的EJB對象。拋出javax.ejb.CreateException異常。
1.無狀態會話Bean 從字面意思來理解,無狀態會話Bean是沒有能夠標識它的目前狀態的屬性的Bean。例如: public class A { public A() {} public String hello() { return Hello 誰?; } }
public class Client { public Client() { A a = new A(); System.out.println(a.hello()); A b = new A(); System.out.println(b.hello()); } } 在Client中生成了兩個A的實例,不管是對象a還是b,它們是沒有狀態的。對於Client來說a和b是沒有差別的(但a != b)。所以同一個無狀態會話Bean的實例都是相同的,可以被不同的客戶端重複使用。
2.狀態會話Bean 至於狀態會話Bean,可以這樣理解:它是有存儲能力的。也就是說至少有一個屬性來標識它目前的狀態。例如: public class B { private String name; public B(String arg) { this.name = arg; } public String hello() { return Hello + this.name; } }
public class Client { public Client() { B a = new B(中國); System.out.println(a.hello()); B b = new B(世界); System.out.println(b.hello()); } }
3.調度池: 其實調度池的概念同數據連接池概念基本相同,都是爲了提高系統性能而設計的。就像是餃子館一樣,餃子的做法可以有兩種,一種是不管當前有沒有客人,先將一些餃子下鍋,等到客人來後可以馬上撈出來給客人吃。還有一種是客人來後纔將餃子下鍋。這個例子顯然不是十分恰當,但它的確能說明問題。調度池的概念就像是餃子的第一種做法一樣,先將一些Bean實例放入池中,(也就是緩存起來,具體緩存多少取決於不同的容器)等到客戶端調用時直接可以取來使用。它和餃子的區別還是很大的,要是餃子下鍋後沒有客人來吃可慘了 :)
4.無狀態會話Bean實現調度池調度: 因爲無狀態會話Bean是不保存會話狀態的,所以無論哪個客戶端調用了某個無狀態會話Bean,都沒有差別,也就是說任何一個無狀態會話Bean的實例都可以爲客戶端程序提供服務。而且無狀態會話Bean同客戶端的關係是1:n。也就是說,同一個會話Bean實例可以供不同客戶端使用。因此無狀態會話Bean可以被存儲在調度池中,供不同客戶端重用。
5.狀態會話Bean實現調度池調度: 因爲狀態會話Bean是保持當前會話的狀態的,所以實現起來遠比無狀態會話Bean困難。因爲它要保存會話狀態,但物理內存的空間是有限的,想要保存n多客戶端當前會話狀態是不可能的。EJB容器採取了同操作系統相同的做法,將狀態會話Bean對話狀態保存在硬盤或其它存儲器中(鈍化passivation)。當被鈍化的Bean原先的客戶端調用其方法時,被鈍化的狀態重新交還給Bean(激活activation)。也許容器把被鈍化的狀態交還給不同的Bean實例,那又有什麼關係呢?只要這個Bean實例能跟原先鈍化之前的那個Bean實例一模一樣就行了。事實上它的確一模一樣:)大多數容器採用最近最少被使用(Least Recently Used:LRU)的鈍化策略。因爲客戶端是不可能一直在執行操作,他可能在看新聞或其它什麼的。所以以這種方式鈍化Bean是非常合理有效的。但有一個例外:正在進行事務處理的Bean一定要等到事務處理完畢後才能被鈍化。大多數容器採用根據需要被激活(Just-in-time)的策略。客戶有請求,ok沒問題,激活那個被鈍化了的Bean:)注:鈍化和激活跟無狀態會話Bean沒關係。
6.對話狀態遵從的規則: Bean遵從Java對象序列化規則(Java Object Serialization)。因爲javax.ejb.EnterpriseBean接口擴展了java.io.Serializable這個接口,所以每個Enterprise Bean類都間接實現了這個接口。
7.狀態會話Bean鈍化時保存的內容如下: a.EJB對象引用。 b.Home對象引用。 d.EJB上下文的引用。 e.JNDI命名上下文。
8.激活和鈍化的回調方法: 每個會話Bean都實現了SessionBean,所以每個會話Bean都有ejbPassivate()和ejbActivate()方法,EJB容器調用ejbPassivate()方法使Bean釋放或處理佔用的資源。調用ejbActivate()方法使Bean恢復在ejbPassivate()過程中釋放的資源。
其實,在EJB中比較複雜的Bean不是會話Bean,不是消息驅動Bean,而是實體Bean。因爲它涉及到對象序列化,O/R Mapping等一些持久化技術(還有一直沒有流行起來的對象數據庫)。
什麼是實體Bean? 實體Bean是有着一組屬性並且每個屬性與數據庫表中的每個字段一一對應並且公開get和set方法供外界訪問。如圖:
每一個屬性對應數據庫表中的一個字段,這樣一個Bean實例就對應了表中的一條記錄。這裏要注意的是,並不是固定的一個實例對應一條記錄。如表中有5條記錄,不一定有5個實體Bean實例來對應,有可能3個,有可能8個。爲什麼呢?
1.因爲對象的創建和刪除是十分消耗系統資源的,所以不可以爲成千上萬條記錄創建成千上萬個對象。解決的辦法是將被實例化的Bean重複再利用,這樣就可以節省這樣的開銷。容器可以動態的分配實體Bean實例給不同的客戶端的EJB對象使用,這樣不但節省了容器不必要地實例化Bean的開銷,而且節省了系統資源。
2.因爲EJB規定只能有一個線程可以運行在一個Bean實例中,也就是說會話Bean,消息驅動Bean,實體Bean都是單線程的。所以爲了使不同的客戶端方便的訪問相同的數據,容器將實例化相同實體Bean的多個實例供不同的客戶端使用。問題來了,當使用這種方法時,怎樣保證數據的同步呢?假如其中一個客戶更改這條數據,如何反映到持有相同實體Bean的不同實例的客戶端呢?爲了達到實體Bean實例緩存的一致性,每個實體Bean都必須公開兩個方法,ejbLoad()和ejbStore()。容器通過調用ejbLoad()方法從存儲空間(數據庫,文件等)中讀取數據。調用ejbStore()方法保存數據到存儲空間中。 實體Bean實例是跟數據庫表中的數據一一對應的。可以把它就看成關係型數據在JAVA中的表現,因爲那沒有什麼區別,改變它就改變了數據庫中的數據。實體Bean是對應到數據庫中的視圖,對象和數據的同步是由EJB容器完成的(調用ejbLoad和ejbStore方法)。容器在實體Bean掛起前調用ejbStore方法,在實體Bean激活之後調用ejbLoad方法。
如何保持實體Bean? 1.可以手工完成持久化操作,也就是說,可以編寫代碼將內存中的字段轉換成數據庫中的字段。這樣,就不得不處理一些如存儲,導入和在實體Bean中查找數據的持久化方法。而且必須編寫持久化API,如JDBC或SQL/J等。實體Bean可以通過JDBC執行SQL INSERT語句在數據庫中插入數據。也可以通過SQL DELETE語句從數據庫中刪除數據。 2.可以讓EJB容器完成持久化操作。容器會自動生成數據庫訪問代碼,執行SQL語句。利用容器管理的持久,我們就可以不用編寫JDBC代碼了。解放了:)
實體Bean是怎樣創建和刪除的? 在EJB中,客戶端是不能直接調用Bean的。它們通過調用EJB對象來執行操作。如下:
客戶端在EJB對象或Home對象上調用remove方法刪除實體Bean數據。ejbRemove()方法並不是將實體Bean從內存中刪除,只是刪除數據庫中的數據。因爲可以重複利用這個Bean實例操作不同的數據庫數據。ejbRemove方法是每個實體Bean必須的方法,它不接收參數。如果客戶端終止連接,並不會調用ejbRemove方法,因爲一個實體Bean的存在時間比客戶端會話時間要長。實體Bean是可以被查找的。利用定位器方法。
實體Bean的上下文 所有的企業Bean都有一個上下文(Context)對象來識別Bean所處的環境。這些上下文中包括EJB容器設置的環境信息。Bean可以訪問這個上下文得到信息。實體Bean上下文中的getEJBLocalObject()方法可以得到當前客戶端與實體Bean關聯的EJB對象(容器產生的EJB對象)。getEJBObject()方法可以得到EJB本地對象。最重要的方法是getPrimaryKey(),它可以得到實體Bean實例的主鍵。主鍵可以唯一標識實體Bean實例,因爲兩個實體Bean數據庫中的數據是不可能有相同的主鍵的。確定實例關聯哪個數據庫數據時就可以調用該方法。當實體Bean掛起一段時間被激活後,實體Bean實例必須對上下文對象執行一個getPrimaryKey()方法調用來確定它將要映射處理的數據庫中的哪些數據。