1 簡(jiǎn)化開(kāi)發(fā)的目標(biāo)
1.1我們的目標(biāo)
EJB3.0是當(dāng)前很多人談?wù)摰脑掝},企業(yè)軟件開(kāi)發(fā)的一個(gè)關(guān)鍵是,提供一個(gè)盡量簡(jiǎn)單的的應(yīng)用框架:它可以使開(kāi)發(fā)人員不用關(guān)注于復(fù)雜的問(wèn)題,比如事務(wù)處理、安全和持久化等。可以集中精力關(guān)注于商業(yè)邏輯,而不用關(guān)心那些低層的技術(shù)細(xì)節(jié),從而提高開(kāi)發(fā)者的效率,得到高質(zhì)量的軟件。這也是制定EJB 3.0規(guī)范的目標(biāo),簡(jiǎn)化開(kāi)發(fā)!
1.2 當(dāng)前的問(wèn)題
EJB3.0希望開(kāi)發(fā)人員能夠從這種新的開(kāi)發(fā)模式中受益,更好地推進(jìn)J2EE的應(yīng)用. 隨著java不斷的進(jìn)步和發(fā)展,越來(lái)越多的企業(yè)選擇J2EE作為它們的解決方案,J2EE體系結(jié)構(gòu)提供一個(gè)簡(jiǎn)化的中間層集成框架來(lái)滿足應(yīng)用的需求。然而,對(duì)于一般的開(kāi)發(fā)人員,目前J2EE 1.4下的EJB 2.1 框架有些過(guò)于復(fù)雜了。 按照EJB2.1規(guī)范的定義,EJB組件必須事先很多的接口, 比如Home接口、Remote接口、local 接口,等等.還要針對(duì)各種應(yīng)用類型定義許多的xml描述文件。當(dāng)我們需要訪問(wèn)某個(gè)組件或者服務(wù)的時(shí)候,必須通過(guò)JDNI查找,通過(guò)名字綁定服務(wù),才能找到我們需要的對(duì)象。
比如我們要使用某個(gè)EJB shopping cart, 就要首先實(shí)現(xiàn)一個(gè)initial context,然后通過(guò)他查找這個(gè)EJB 的home 接口,然后調(diào)用home街口的create方法,得到一個(gè)EJB object, 最后調(diào)用這個(gè)EJB object的商務(wù)方法. 下面是一個(gè)例子:
// EJB 2.1 Client view of the ShoppingCart Bean
...
Context initialContext = new InitialContext();
ShoppingCartHome myCartHome = (ShoppingCartHome)
initialContext.lookup(“JAVA:comp/env/ejb/cart”);
ShoppingCart myCart= myCartHome.create();
//Use the Bean
Collection widgets = myCart.startToShop(Widgets )
...
// Don't forget code to handle JAVAx.ejb.CreateException
...
EJB2.1的規(guī)范還要求我們必須實(shí)現(xiàn)Javax.EJB里面定義的接口, 實(shí)現(xiàn)里面的Methods比如 EJBCreate(), EJBPassivate(), and EJBActivate()。大多數(shù)情況下這些方法是不需要開(kāi)發(fā)人員作任何修改的。這些規(guī)定實(shí)際上都和真正核心的商務(wù)邏輯沒(méi)什么關(guān)系,都只是一些技術(shù)模板,規(guī)定了開(kāi)發(fā)人員必須按照這樣的模板進(jìn)行開(kāi)發(fā). 比如下面這段代碼:
人們開(kāi)始思考,怎么樣才能把EJB的開(kāi)發(fā)變的更加簡(jiǎn)單,更好的利用這種技術(shù). 當(dāng)前正在制定的EJB 3.0的標(biāo)準(zhǔn)的目標(biāo)就是簡(jiǎn)化開(kāi)發(fā), 讓更多的開(kāi)發(fā)人員被它的易用和強(qiáng)大功能所吸引過(guò)來(lái),喜愛(ài)這項(xiàng)技術(shù)。為了達(dá)到這個(gè)目標(biāo),要做的第一件事情,也是最重要的事情,就是從一個(gè)開(kāi)發(fā)人員的角度,將EJB的使用盡量的簡(jiǎn)化。
1.3 JCP專家組的工作
這個(gè)工作是由EJB3.0 專家組來(lái)完成的。我們知道JAVA的開(kāi)發(fā)和推動(dòng)是由一個(gè)開(kāi)放的組織來(lái)完成的,這個(gè)組織的名字叫做JAVA Community PRocess。簡(jiǎn)稱JCP. SUN的理念是: 創(chuàng)新無(wú)處不在. 所以JCP小組從世界的每個(gè)角落聽(tīng)取關(guān)于JAVA的建議,將各方面對(duì)JAVA的要求通過(guò)制定JSR(JAVA Specification Request )形式確定下來(lái). EJB 3.0規(guī)范的JSR編號(hào)是 220. 整個(gè)專家組的制定成員包括J2EE 注冊(cè)用戶, 應(yīng)用服務(wù)器的開(kāi)發(fā)廠商和J2EE社區(qū)的成員.
簡(jiǎn)化一個(gè)現(xiàn)有的技術(shù),尤其是得到廣泛的開(kāi)發(fā)人支持的技術(shù),比如EJB, 不是一個(gè)簡(jiǎn)單的工作. 作為鋪墊,專家組進(jìn)行了大量的準(zhǔn)備工作,檢驗(yàn)了EJB技術(shù)的復(fù)雜性,當(dāng)前EJB流域流行的各種模式和反模式,以及從客戶和開(kāi)發(fā)人員來(lái)的各種需求。開(kāi)發(fā)人員和用戶根據(jù)實(shí)際的需要,希望EJB能夠提供他們滿意的特性.
檢驗(yàn)結(jié)果發(fā)現(xiàn),大多數(shù)情況下,人們不需要更高級(jí)的技術(shù),而是需要更簡(jiǎn)化的技術(shù),來(lái)簡(jiǎn)化當(dāng)前的開(kāi)發(fā)模式。而不是像以前的EJB發(fā)布一樣,在技術(shù)復(fù)雜度上的提高。定義一個(gè)新技術(shù),不僅對(duì)老的技術(shù)有一定的更新, 也要能充分并容老的技術(shù), 提供一定的向后兼容性. 因?yàn)椴捎眠@些技術(shù)的企業(yè),已經(jīng)在這些技術(shù)上投資了很多. 如果因?yàn)榧夹g(shù)的更新就使得對(duì)應(yīng)的IT系統(tǒng)和數(shù)據(jù)變得不能使用,是一件非常糟糕的事情.
所以,定義了EJB3.0規(guī)范的同時(shí),如何支持現(xiàn)有的EJB技術(shù)是非常必要的. 要保證現(xiàn)有的EJB API是持續(xù)可用的, 還要和新的EJB3.0 API結(jié)合起來(lái), 對(duì)早期的API應(yīng)該繼續(xù)提供支持而不是標(biāo)記為不贊成使用
1.4 標(biāo)注的新功能
EJB3.0的很多新特性是通過(guò)JAVA SE5.0來(lái)實(shí)現(xiàn)的。這里我們就要談到JAVA SE 5.0,它所提供的許多特性,其中最有趣的一點(diǎn)就是標(biāo)注(Annotation)的功能。我們知道以前的JAVA語(yǔ)言都是命令格式的, 比如a.b(), 表示讓類a做事情b, 但是很多時(shí)候我們只是需要對(duì)某個(gè)對(duì)象做一些注解,比如對(duì)某個(gè)類標(biāo)記為可持續(xù)化的Serializable. 這只是一個(gè)標(biāo)記,為了以后的處理提供說(shuō)明,本身不需要做任何操作。
在Deploy 的時(shí)候, 提供了很多說(shuō)明的XML文件,比如部署描述文件,里面說(shuō)明了引用的EJB的名字,接口, 以及當(dāng)前EJB的Transaction Type等等信息. 所有這些信息都是說(shuō)明性的,而不是命令性的. 用來(lái)對(duì)某個(gè)對(duì)象的某個(gè)屬性坐一段說(shuō)明. 因此,有一個(gè)非常有趣的想法,能不能通過(guò)對(duì)JAVA語(yǔ)言的擴(kuò)展,結(jié)合標(biāo)注和命令這兩者的優(yōu)點(diǎn) ?
這也是有一個(gè)專家組在JCP的組織內(nèi)完成的, JSR規(guī)范的編號(hào)是JSR 175, 為JAVA SE 5.0支持注解(Annotation)的功能. 這個(gè)規(guī)范為EJB3.0 的簡(jiǎn)化實(shí)現(xiàn)提供了一些基本的支持, 也是最關(guān)鍵的支持. 標(biāo)注可以有自己的屬性,也可以定義自己的持續(xù)時(shí)間,表示這段信息是否保存到 源代碼中,還是一直持續(xù)到Class中,或者一直保持到運(yùn)行時(shí)間. 標(biāo)注有自己的缺省值.大多數(shù)情況下,無(wú)須說(shuō)明我們就可以推算出來(lái)這個(gè)對(duì)象的行為。
2 輕松的實(shí)現(xiàn)開(kāi)發(fā)
2.1 減輕開(kāi)發(fā)人員的負(fù)擔(dān)
EJB3.0的簡(jiǎn)化工作包括下面幾個(gè)部分:
提供一個(gè)簡(jiǎn)化的API, 包括對(duì)EJB的定義,對(duì)EJB的引用等等
減少開(kāi)發(fā)的類數(shù)目,不再需要那么多的interface
相關(guān)性注入
簡(jiǎn)化的查詢機(jī)制
從開(kāi)發(fā)人員的角度不必要使用部署描述文件, 很多的工作可以放到代碼里面用標(biāo)注來(lái)說(shuō)明,比如Entity Bean 的Transaction Type
簡(jiǎn)化的持久化功能
簡(jiǎn)化和改善數(shù)據(jù)對(duì)象的O/R Mapping.
標(biāo)注可以應(yīng)用到編程語(yǔ)言的一些基本元素上,比如類,方法,變量,包等等. 當(dāng)我們?cè)诖a中使用了這些標(biāo)注, 根據(jù)這個(gè)標(biāo)注對(duì)應(yīng)的持續(xù)策略, 它可以被編譯到Class 文件中去,或者一直保持到運(yùn)行的時(shí)候。大多數(shù)在 EJB 3.0 中定義的標(biāo)注都是Runtime保持策略, 這樣做的好處是提供了最大的靈活性。而且由于大量工作放到了運(yùn)行的時(shí)候來(lái)做,也減少一部分Deploy的工作。
我們通過(guò)定義缺省的語(yǔ)法來(lái)說(shuō)明大多數(shù)常見(jiàn)的情況。開(kāi)發(fā)人員不需要再專門(mén)說(shuō)明常見(jiàn)的情況,“OK, 沒(méi)問(wèn)題,缺省的設(shè)置就已經(jīng)可以滿足需要了“ 這樣,開(kāi)發(fā)人員的工作大大減輕了。
這也引出來(lái)了EJB3.0中的一個(gè)很有意思的概念 "Configuration By Exception" --只有在例外的情況下才需要我們的參與.
EJB3.0的目標(biāo)是簡(jiǎn)化開(kāi)發(fā)人員的工作,讓他們專注于商務(wù)應(yīng)用的開(kāi)發(fā)而不是把精力放到很多繁瑣的例行工作上,這些工作可以交給Container來(lái)完成。EJB通過(guò)注入來(lái)指定自己需要的資源,不用再寫(xiě)那些麻煩的方法. 將對(duì)象的創(chuàng)建和獲取提取到外部。由外部容器提供需要的組件。這樣,開(kāi)發(fā)人員只用在開(kāi)始的時(shí)候定義,說(shuō)我需要這個(gè)資源, 后面就可以直接使用這個(gè)資源, 這樣會(huì)大大的簡(jiǎn)化開(kāi)發(fā), 因?yàn)殚_(kāi)發(fā)人員只用關(guān)心如何使用這個(gè)對(duì)象和商務(wù)方法, 而不用擔(dān)心其他的技術(shù)細(xì)節(jié)。
2.2 拋開(kāi)繁瑣的細(xì)節(jié)
下面我們看看都作了那些簡(jiǎn)化。我們的目的是把那些繁瑣的技術(shù)細(xì)節(jié)隱藏起來(lái),程序開(kāi)發(fā)人員只用關(guān)心自己的商務(wù)邏輯代碼,而不用關(guān)心那些復(fù)雜的技術(shù)模板,必須實(shí)現(xiàn)的接口等,哪怕這些方法和接口根本不需要實(shí)現(xiàn).
不再需要EJB的部件接口
每個(gè)EJB 都只是一個(gè)普通的JAVA Class
不再需要home接口, 我們不再用home 來(lái)創(chuàng)建這個(gè)EJB
不再需要實(shí)現(xiàn)javax.ejb.EnterpriseBean借口
對(duì)于需要在回調(diào)方法里實(shí)現(xiàn)的部分,我們采用標(biāo)注的方式說(shuō)明一個(gè)方法為回調(diào)方法
不再需要使用復(fù)雜的JNDI名字調(diào)用機(jī)制,對(duì)于需要服務(wù)或者資源的地方
我們采用了相關(guān)性注入的方法,另外也可以通過(guò)簡(jiǎn)化的lookup方法來(lái)查找資源
下面讓我們看一個(gè)簡(jiǎn)單的無(wú)狀態(tài)sessionBean的例子。無(wú)狀態(tài)session Bean是最簡(jiǎn)單也是最常用 Bean,很多初學(xué)EJB的人都從無(wú)狀態(tài)Session Bean開(kāi)始。如何讓無(wú)狀態(tài)Session Bean 變的簡(jiǎn)單易用成為一個(gè)非常有意義的話題。
前面假設(shè)我們已經(jīng)定義了相關(guān)的interface, 這個(gè)EJB2.1的的功能是對(duì)員工的工資做處理,打開(kāi)一個(gè)數(shù)據(jù)庫(kù)連接,進(jìn)行員工工資信息的某些操作,等等.
// EJB 2.1
public Class PayrollBean implements JAVAx.ejb.SessionBean
{
SessionContext ctx;
DataSource empDB;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
public void ejbCreate() {
Context initialContext = new InitialContext();
empDB = (DataSource)initialContext.lookup( JAVA:comp/env/jdbc/empDB );
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
public void setBenefitsDedUCtion (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}
// NOTE deployment descriptor needed
這里我們首先要實(shí)現(xiàn)一個(gè) sessionBean interface,保持一個(gè)對(duì)sessioncontext的引用,然后是在EJBcreate 方法里面我們調(diào)用JNDI得到一個(gè)datasource。 后面我們必須要定義一些回調(diào)方法,雖然這些方法我們不會(huì)實(shí)現(xiàn)任何邏輯。然后在 商務(wù)方法里面,我們打開(kāi)一個(gè)數(shù)據(jù)庫(kù)連接。
注意,我們還沒(méi)有完。為了使用這個(gè)EJB, 必須加上x(chóng)ml的部署描述文件打好包。
下面是一個(gè)常見(jiàn)的部署描述文件可以是這樣子的。
<session>
<ejb-name>PayrollBean</ejb-name>
<local-home>PayrollHome</local-home>
<local>Payroll</local>
<ejb-class>com.example.PayrollBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<resource-ref>
<res-ref-name>jdbc/empDB</res-ref-name>
<res-ref-type>javax.sql.DataSource</res-ref-type>
<res-auth>Container</res-auth>
</resource-ref>
</session>
...
<assembly-descriptor>
...
</assembly-descriptor>
從開(kāi)發(fā)人員的角度來(lái)檢查這樣一個(gè)簡(jiǎn)單的EJB 是非常有意義的。我們可以更好的理解有那些地方可以有改動(dòng)。一個(gè)主要的問(wèn)題是,定義了這么多的方法和結(jié)構(gòu)以后,程序的清晰化受到了影響,結(jié)構(gòu)變得混亂。真正需要關(guān)注的商務(wù)方法沒(méi)有很好的強(qiáng)調(diào)和體現(xiàn)。
下面讓我們看一看EJB 3.0 是如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的stateless session Bean的。
// Same example, EJB 3.0
@Stateless public class PayrollBean implements Payroll {
@Resource DataSource empDB;
public void setBenefitsDeduction (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}
@Stateless 標(biāo)注表示這是一個(gè)stateless session Bean. 使用這樣的標(biāo)注可以讓我們不再需要使用SessionBean 接口, 這樣就大大的簡(jiǎn)化了EJB的實(shí)現(xiàn)類。 Bean的商務(wù)接口是payroll. 這是一個(gè)普通的JAVA interface. 缺省的情況,container會(huì)把他作成一個(gè)local interface. 如果需要實(shí)現(xiàn)一個(gè)遠(yuǎn)程接口,只需再定義一個(gè)標(biāo)注 @Remote. 注意在接口里面不需要定義 RemoteExceptions,它由Container 層在后面處理掉了
2.3 引用對(duì)象的新方法
在老的EJB規(guī)范中,還有一個(gè)比較復(fù)雜的地方是訪問(wèn)環(huán)境對(duì)象。EJB 2.1 里面訪問(wèn)環(huán)境要首先在組件定義的相關(guān)性引用,比如resource-refs, EJB-refs, 然后在JNDI名字空間里面配置這些環(huán)境對(duì)象。 最后查找運(yùn)行的時(shí)候JNDI空間里面查找這些環(huán)境對(duì)象
EJB 3.0對(duì)此提供了兩種簡(jiǎn)化的方案:
1) 相關(guān)性注入
2) 一個(gè)簡(jiǎn)化的查詢lookup方法
相關(guān)性注入是一種技術(shù),開(kāi)發(fā)人員在原代碼中加入標(biāo)注的一個(gè)定義,說(shuō)明需要這個(gè)環(huán)境對(duì)象,然后由Container在初始化的時(shí)候把真的環(huán)境對(duì)象注入里面。
目前EJB3.0 spec里面有兩種注入方式,setter注入和變量注入, 這些在以后的規(guī)范中可能會(huì)有變化,比如一種統(tǒng)一的注入方式。 我們可以通過(guò)注入標(biāo)記@EJB來(lái)定義一個(gè)EJB的引用,也可以通過(guò)注入標(biāo)記@resource 表示我們要引用一個(gè)資源,它可以是EJB以外的一切環(huán)境對(duì)象. 專家組為了盡可能的簡(jiǎn)化開(kāi)發(fā),將使用注入的對(duì)象類型作了簡(jiǎn)化。
EJB3.0 將仍然提供一個(gè)動(dòng)態(tài)查詢的方法,但是從程序開(kāi)發(fā)人員的角度,不需要再使用JNDI API,而是采用更為簡(jiǎn)化的EJB Context 的 Lookup方法。
在新的EJB 3.0規(guī)范中,因?yàn)椴辉傩枰獜?fù)雜的home接口和EJB 接口。EJB client端的編碼也大大的簡(jiǎn)化了,和訪問(wèn)一個(gè)普通的JAVA 對(duì)象沒(méi)有什么區(qū)別。上面的那個(gè)EJB2.1的例子比較起來(lái),在EJB 3.0 里面使用一個(gè)EJB變的非常簡(jiǎn)單,只需要兩行代碼:
// EJB 3.0 client view
@EJB ShoppingCart myCart;
...
Collection widgets = myCart.startToShop(Widgets );
...
首先定義一個(gè)EJB的應(yīng)用,然后就可以直接調(diào)用這個(gè)EJB的商務(wù)方法,剩下的工作由Container來(lái)為我們完成。
2.4 新的事務(wù)管理
EJB中的事務(wù)(Transaction)管理大大簡(jiǎn)化了用戶開(kāi)發(fā)程序。把應(yīng)用分成一個(gè)一個(gè)小的單元,叫做transaction. 事務(wù)系統(tǒng)保證了這個(gè)單元里面的任務(wù)是完整的,要么全部執(zhí)行,要么出錯(cuò)后完全退回到初始狀態(tài)。
J2EE中有兩種類型的事務(wù), 容器管理的和Bean管理的。他們?cè)谌绾螁?dòng)和結(jié)束事務(wù)上是不同的。Bean 管理的事務(wù)由組件使用 UserTransaction類顯式啟動(dòng)和結(jié)束的。代碼中需要調(diào)用方法 UserTransaction.begin() 和 UserTransaction.commit() 。Container 管理的事物是由container 自動(dòng)來(lái)完成的。
針對(duì)不同的事務(wù)類型,可以定義6種不同的事務(wù)屬性。
事務(wù)屬性告訴 Container 是否把EJB方法里面的工作放到用戶的事務(wù)里面。還是針對(duì)這個(gè)方法重新啟動(dòng)一個(gè)新的事務(wù). 或者執(zhí)行這個(gè)方法而不包含在事務(wù)里面,等等。
在EJB3.0規(guī)范中,我們?nèi)笔〉亩x是容器管理的事務(wù). 而且針對(duì)所有的Bean方法,應(yīng)用Required Transaction屬性,它的意思是如果調(diào)用這個(gè)方法的應(yīng)用沒(méi)有Transaction Environment,那么這個(gè)方法會(huì)自動(dòng)創(chuàng)建一個(gè)新的。
開(kāi)發(fā)人員可以使用標(biāo)注的方式在針對(duì)整個(gè)EJB或者某個(gè)具體的方法指定他的Transaction Attribute.
首先我們看一個(gè)工資處理的EJB的例子, 這是一個(gè)標(biāo)準(zhǔn)的EJB 3.0 Stateless Session Bean。缺省情況下,每個(gè)方法是都是由container來(lái)管理Transaction的,缺省的事務(wù)屬性是required
// Uses container-managed transction, REQUIRED attribute
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empId) {...}
public void setSalary(int empId, double salary) {...}
}
下面還是這個(gè)例子,我們知道對(duì)于有關(guān)重要數(shù)據(jù)的改動(dòng),總是非常敏感的。比如我們?cè)诟哪硞€(gè)用戶的工資的時(shí)候同時(shí)修改他的所得稅,我們希望這兩個(gè)調(diào)用是在同一個(gè)Transaction里面發(fā)生的。
@Stateless public PayrollBean implements Payroll {
@TransactionAttribute(MANDATORY)
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
@TransactionAttribute(MANDATORY)
public void setSalary(int empId, double salary) {...}
}
那么調(diào)用用戶的工資改動(dòng)的方法就必須在一個(gè)已經(jīng)存在的Transaction Environment
中,為此,我們用@transactionattribute(mandatory)標(biāo)注 這個(gè)方法必須在一個(gè)客戶端的transaction中,如果客戶端沒(méi)有這樣的一個(gè) Transaction Context, Container會(huì)扔出來(lái)一個(gè) Javax.ejb.EjBTransactionRequiredException 的錯(cuò)誤信息。
2.5 新的安全機(jī)制
EJB架構(gòu)不鼓勵(lì)開(kāi)發(fā)人員用代碼的方式實(shí)現(xiàn)安全機(jī)制,而是采用安全角色的方法,通過(guò)定義可以訪問(wèn)的安全角色,來(lái)限制對(duì)某個(gè)方法的訪問(wèn)權(quán)限。
缺省情況,在3.0里面所有的方法都是"unchecked"。也就是說(shuō)缺省情況下對(duì)所有方法是不用安全控制策略的。如果調(diào)用某個(gè)方法的客戶端具有某個(gè)用戶角色(Role) ,我們可以制定是否這個(gè)方法也是沿用這個(gè)角色。缺省情況下的安全策略是"Caller Identity",也就是說(shuō)被調(diào)用者的角色和調(diào)用者的角色應(yīng)該是一致的
這是一個(gè)EJB 3.0的安全實(shí)現(xiàn)的例子。我們對(duì)于其他的方法都沒(méi)有設(shè)定安全策略。但是對(duì)于設(shè)定某個(gè)員工的工資是多少,這樣的安全要求比較高的方法設(shè)定了只有人事部門(mén)的管理員才能調(diào)用。我們采用了一個(gè)
@RolesAllowed("HR_PayrollAdministrator ")
// Security view
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
// salary setting is intended to be more restricted
@RolesAllowed( HR_PayrollAdministrator )
public void setSalary(int empId, double salary) {...}
}
2.6 事件的通知和檢查
EJB 3.0中, 開(kāi)發(fā)人員不需要實(shí)現(xiàn)那些不必要的callback methods。 他可以把任意方法指定為一個(gè)事件通知方法。通過(guò)標(biāo)記一個(gè)通知標(biāo)注,我們把一個(gè)方法標(biāo)記為一個(gè)回調(diào)方法, 例如:
@Stateful public class AccountManagementBean
implements AccountManagement {
Socket cs;
@PostConstruct
@PostActivate
public void initRemoteConnectionToAccountSystem {
...
}
@PreDestroy
@PrePassivate
public void closeRemoteConnectionToAccountSystem {
...
}
...
}
在這個(gè)EJB 3.0 的例子當(dāng)中, 我們定義了一個(gè)有狀態(tài)的session Bean,把初始化的工作都交給init remote connection方法,同時(shí)標(biāo)記他為一個(gè)回調(diào)方法,在construct ,和 activite之后調(diào)用
同時(shí)我們把清理的工作都交給close remote connection方法,同時(shí)標(biāo)記他為一個(gè)回調(diào)方法,在destroy 和passivate之前調(diào)用
對(duì)于那些高級(jí)的用戶,需要定制自己的事件檢查和偵聽(tīng)機(jī)制。檢查方法和所被檢查的對(duì)象方法可以在同一個(gè) Bean 中,也可以在不同的JAVA Class里面。 這種檢查機(jī)制有下面的特點(diǎn):
在方法周圍進(jìn)行檢查
包裝商務(wù)方法的整個(gè)調(diào)用過(guò)程
可以對(duì)方法調(diào)用的參數(shù)和結(jié)果進(jìn)行處理
檢查類的序列中,可以拿到上下文數(shù)據(jù)
多個(gè)檢查類可以按照指定的順序執(zhí)行
可以用部署表述符來(lái)指定執(zhí)行順序
我們用 @Interceptors 標(biāo)注指定一個(gè)外部的檢查方法類, 用 @AroundInvoke制定內(nèi)部的某個(gè)方法為檢查方法。在檢查方法里面,我們用proceed()來(lái)調(diào)用具體的商務(wù)方法。目前的檢查方法是檢查一個(gè)Bean里面的所有方法,專家組正在制定標(biāo)準(zhǔn), 讓它可以具體到檢查制定的某個(gè)方法。
下面是一個(gè)EJB3.0的檢查方法的例子:
我們制定了這個(gè)無(wú)狀態(tài)session Bean檢查方法類是這三個(gè)類.
accountaudit, metrics, customsecurity。那么實(shí)際執(zhí)行的時(shí)候會(huì)按照這個(gè)制定的順序來(lái)實(shí)行
檢查.
@Interceptors({
com.acme.AccountAudit.class,
com.acme.Metrics.class,
com.acme.CustomSecurity.class
})
@Stateless
public class AccountManagementBean
implements AccountManagement {
public void createAccount(int accountId, Details details) {...}
public void deleteAccount(int accountId) {...}
public void activateAccount(int accountId) {...}
public void deactivateAccount(int accountId) {...}
...
}
2.7 部署描述文件的優(yōu)先級(jí)
在EJB3.0中,我們可以用標(biāo)注的方法來(lái)指定對(duì)環(huán)境對(duì)象的引用,也可以用部署描述符文件(Deployment Description)的方式來(lái)制定對(duì)環(huán)境對(duì)象的應(yīng)用, 也可以兩個(gè)同時(shí)使用. 如果我們?cè)诓渴鹈枋鑫募痛a標(biāo)注中都制定了環(huán)境對(duì)象,那么部署描述文件中的那個(gè)引用有更高的優(yōu)先級(jí), 這樣就給了應(yīng)用的Deployer相對(duì)比較大的靈活性來(lái)控制.
3 持久化的魔力
3.1 最初的目標(biāo)
EJB 3.0 專家組的另外一個(gè)目標(biāo)是為實(shí)體Bean( Entity Bean ) 和對(duì)象/關(guān)系的映射,提供一個(gè)輕量級(jí)的模型。實(shí)際上,目前關(guān)于實(shí)體Bean的爭(zhēng)論很多, 很多批評(píng)人士對(duì)它的架構(gòu)感到不滿,我們的目標(biāo)是改善EJB 容器管理的持久化模型,從各地的一些優(yōu)秀的開(kāi)源軟件中吸取靈感,從一些反模式(Anti-Pattern)中吸取經(jīng)驗(yàn), 從而讓新的標(biāo)準(zhǔn)稱為技術(shù)上的領(lǐng)跑者。 EJB3.0的持續(xù)化包括下面的一些特性:
簡(jiǎn)化實(shí)體bean的編程方式,減少不必要的開(kāi)發(fā)接口
改善EJB的持久化, 為O/R映射提供繼承和多態(tài)的信息
可以在EJB Container之外使用實(shí)體Bean
不需要Container 就可以對(duì)商務(wù)方法進(jìn)行測(cè)試
不再需要數(shù)據(jù)傳輸對(duì)象DTO (Data Transfer Objects) 之類的設(shè)計(jì)模式(Design Pattern)
改善的EJB QL
為了解決這些意見(jiàn),ejb 3.0 的專家組集中在一個(gè)經(jīng)過(guò)簡(jiǎn)化的持久化模型,目前業(yè)界已經(jīng)有類似的產(chǎn)品和模式,比如Hibernate和Toplink, 這是一個(gè)全新的方向,代表著輕量級(jí)的對(duì)象/關(guān)系(O/R)映射模型。
在EJB3.0中,實(shí)體Bean是普通的Java 類, 這是一些真正的類, 而不是抽象類。而且更為簡(jiǎn)化的是,開(kāi)發(fā)人員不再需要實(shí)現(xiàn)任何接口,不論是商務(wù)借口或者是回調(diào)接口。
而且不再需要實(shí)現(xiàn)數(shù)據(jù)傳輸對(duì)象(DTO), 因?yàn)楝F(xiàn)在的Entity Bean本身就是一個(gè)簡(jiǎn)單的普通JAVA 對(duì)象POJO (Plain Old Java Object), 標(biāo)記為序列化之后就可以在客戶端和服務(wù)端進(jìn)行傳遞,不再需要特殊的處理。
3.2 管理類的角色
在新的EJB3.0規(guī)范里面,我們看到了一個(gè) EntityManager 類, 對(duì)于實(shí)體Bean而言,這是一個(gè)類似于調(diào)用工廠(Factory)或者統(tǒng)一的Home接口之類的角色。Entity manager自己的生命周期可以由Container或者應(yīng)用程序都可以來(lái)管理。
Entity Manager 將負(fù)責(zé)跟蹤數(shù)據(jù)庫(kù)事務(wù)上下文中, 實(shí)體bean 對(duì)象的狀態(tài)。對(duì)于開(kāi)發(fā)人員來(lái)說(shuō), javax.persistence.EntityManager 成為對(duì)實(shí)體bean的統(tǒng)一訪問(wèn)點(diǎn)。 可以把它看作是對(duì)實(shí)體Bean操作的一個(gè)”home”. 我們要通過(guò)entity manager 調(diào)用實(shí)體bean的生命周期管理。 比如:Persist, Remove, Merge, Flush, Refresh, 等方法。
Entity Manager是所有Entity Bean的持久化管理接口,任何對(duì)Entity Bean的操作都必須通過(guò)它來(lái)進(jìn)行。有的開(kāi)發(fā)人員會(huì)對(duì)這個(gè)接口感到熟悉,因?yàn)樗cHibernate的Session接口和JDO的Persistence Manager非常相似。Entity Manager的接口主要方法如下:
package javax.ejb;
public interface EntityManager {
public void create(Object entity);
public < T > T merge(T entity);
public void remove(Object entity);
public Object find(String entityName, Object primaryKey);
public < T > T find(Class < T > entityClass, Object primaryKey);
public void flush();
public Query createQuery(String ejbqlString);
public Query createNamedQuery(String name);
public Query createNativeQuery(String sqlString);
public void refresh(Object entity);
public void evict(Object entity);
public boolean contains(Object entity);
}
EntityManager 還大大方便了查找方法的實(shí)現(xiàn),記得我們?cè)贓JB2.1里面是怎么做的嗎?在EJB 3.0里面,可以直接調(diào)用EntityManager.find(String entityName, Object primaryKey),查找具有某個(gè)主鍵的實(shí)體 bean 實(shí)例。例如:
public OrderBean findByPrimaryKey(String orderId)
{ return (OrderBean)em.find("OrderBean" , orderId);
}
EntityManager作為Query對(duì)象的生產(chǎn)工廠, 可以用createQuery(String ejbQlString) 創(chuàng)建一個(gè)EJB QL 查詢,也可以用createNamedQuery(String queryName)來(lái)創(chuàng)建一個(gè) NamedQuery 查詢。下面是一個(gè)例子:
public List findWithAddr(String addr) {
return em.createQuery(
"SELECT o FROM Orders o WHERE o.addr LIKE :orderAddress")
.setParameter("orderAddress", addr)
.setMaxResults(100)
.listResults();
}
3.3 O/R Mapping的標(biāo)注
O/R Mappings 標(biāo)注的元數(shù)據(jù),使得用戶可以修飾他們的EJB3.0 Entity Bean. 在 EJB 3.0缺省環(huán)境下,表的名字就是類的名字, 兩者是一致的, public Java Bean getter方法假定為訪問(wèn)表中同樣名字的屬性值, 開(kāi)發(fā)人員可以通過(guò)@Table, @column等各種不同的標(biāo)注來(lái)修改這個(gè)缺省的定義. 例子:
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",
pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }
@Entity 說(shuō)明這是一個(gè)Entity Bean, Table標(biāo)記了對(duì)應(yīng)的O/R Mapping的主表, 而 SecondaryTable標(biāo)記了 Entity對(duì)應(yīng)的輔助表,
持續(xù)化的API, 和查詢語(yǔ)言,以及O/R Mapping標(biāo)注, 都是EJB 3.0規(guī)范的一部分。
持久化API的設(shè)計(jì)目標(biāo)是能夠獨(dú)立于Container的運(yùn)行,只需要有一個(gè)Java SE環(huán)境就可以運(yùn)行了。
4 小結(jié)
本文以描述了EJB 3.0規(guī)范的一些新特性。EJB 3.0將是EJB歷史上最大的一次改動(dòng),它充分吸收了一些開(kāi)源項(xiàng)目,比如Spring、Hibernate的經(jīng)驗(yàn),變得更加方便實(shí)用,體現(xiàn)了簡(jiǎn)化開(kāi)發(fā)的設(shè)計(jì)目標(biāo)。這篇文章希望能夠給大家?guī)?lái)一點(diǎn)關(guān)于EJB 3.0的印象,目前EJB 3.0規(guī)范已經(jīng)進(jìn)入了Proposed Final Draft階段,當(dāng)然,將來(lái)這個(gè)規(guī)范的技術(shù)細(xì)節(jié)還可能發(fā)生變化。
(出處:http://www.survivalescaperooms.com)
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注