一段時間以來,EJB、Hibernate、Spring的恩怨情仇,是J2EE的熱門話題。EJB VS Hibernate、EJB VS Spring這
樣的議題隨處可在。這篇文章,筆者試圖通過對技術發展史的回顧,對source的剖析、對比,深入挖掘這些技術出現的初衷、缺陷、走向。
一、 前言
我強調EJB、Hibernate、Spring的恩怨情仇,同時也必須說明,我一向反感你說我怎麼侵入、你說我怎麼依賴式的EJB VS Hibernate、EJB VS Spring的討論,因爲這種行爲本身就是沒有意義的、錯誤的。我提倡從正確的技術對比和理性的技術批判中受益。對比,我們需要找準對比點;批判,我們需要從source、spec、application context中分析、批判。
二、 從EJB說起
2.1 EJB幾種Bean類型的引入順序
EJB1.0,有兩種Bean類型:SessionBean、EntityBean。
EJB2.0,引入CMP EntityBean、引入Message-Driven Bean、引入Local接口。
2.2 Entity Bean和O/R Mapping的微妙關係
我想對O/R Mapping、O/R Mapping Engine做一個簡要的說明。
O/R Mapping,以對象視圖(Object View)來看待DB Record,對象操作能夠通明地映射成DB Record操作。
O/R Mapping Engine,就是使得O/R Mapping成爲可能的具體實現手法。
從我們的定義來看,使用BMP EntityBean意味着你自己在實施一個非常簡單的O/R Mapping ,你自己在爲能夠以對象視圖和DB交互做出努力。而爲了支持CMP EntityBean,EJB Server提供商會爲你提供O/R Mapping 能力。而且,事實的確是這樣,任何支持CMP EntityBean的EJB Server都需要提供一個Persistence(O/R Mapping) Engine,譬如JBOSS的JAWS(Just Another Web Store)。
至於,Hibernate、IBATIS等,雖然,也叫做O/R Mapping Tool,但是它們的意義已經遠遠超過了CMP EntityBean O/R Mapping Engine這樣的Tool。下面會有詳細的分析。
2.3 EJB-1.0
EJB1.0是分佈式組件架構,包括SessionBean和EntityBean。它引入了很多非常前衛的技術、概念。主要包括分佈式組件、容器、DB操作的對象試圖。
EJB1.0,可能Expert Group設想的EJB的應用場景是大規模分佈式的系統。所以,SessionBean、EntityBean尚沒有引入Local接口。 client->SessionBean、client->EntityBean、SessionBean->SessionBean、SessionBean->EntityBean等等都是remote的。
EJB1.0,將容器這個概念引入EJB,意義重大。這裏我想順便澄清一個問題:容器是什麼?我的觀點:容器只是一個概念、一種架構。就拿EJB Server來說,Server試圖爲Bean提供分佈式、事務、安全等基礎設施,那麼就必須有一個凌駕於Bean之上的Layer或者說warp,這樣才能夠從高層攔截Bean調用,進行一些額外操作。這樣的架構就叫做容器架構,這個概念當然不是自EJB纔有的。至於怎樣實現,方法各異。
事實上,以個人的觀點,容器架構的核心,不在於從高層“掌握”Beans(Objects),而在於採用怎樣的技術來實現Bean(Object)調用的攔截。無疑,EJB Servers和Spring的實現手法是不同的。下面會詳細討論這個問題。 |
EJB1.0爲DB操作提供了對象試圖。Expert Group當初是怎樣定位EntityBean的?無疑,1.0中的EntityBean,也就是2.0以後的BMP EntityBean,定位是Domain Object(我不知道當時有沒有這個概念,只是它們的思想是非常一致)。它的fields直接映射DB Table Schema,member functions就是對Table Record的操作。Client->EntityBean、SessionBean->EntityBean等就可以直接和數據庫交互了。
有人跟我說EJB1.0基於Catalysis方法學,SessionBean對應Role,EntityBean對應Domain Object。到目前爲止,我對這種說法,持保留態度,因爲EJB Spec中,我絲毫沒有這種說法的痕跡。
2.4 EJB-2.X
無疑,EJB1.X的設計存在重大的缺陷。2.0增加的特性包括Local接口、CMP EntityBean,它們是對1.x缺陷的重大更正。
首先,事實上沒有多少Expert Group想象中的大規模分佈式應用。我們從兩個方面來說:(1)通過Remote方式使用 EntityBean引起嚴重的性能問題,很有必要提供Local接口。(2)訪問SessionBean的WebApplication和SessionBean部署在同一服務器上的情況非常普遍,所以提供SessionBean的Local接口,也是必然的事情。2.X之後,最常用的一個Pattern就是使用SessionBean Façade通過Local的形式訪問EntityBean。而且,即使應用規模大到連SessionBean和EntityBean都需要部署到不同的Server,也沒有關係,因爲EJB2.X同樣支持Remote接口。
其次,EJB2.0引入CMP EntityBean。CMP EntityBean解決了EntityBean持久化表示和JDBC分離的問題,同時大大簡化了EntityBean的開發、提高了性能。但是,我不得不說,CMP EntityBean將EJB1.X的Domain Model理念完全沖掉了,因爲CMP EntityBean是不能包含任何Domain Logic的。BMP EntityBean似乎就是Matrin所說的DomainObject,而CMP EntityBean在典型的SessionBean->EntityBean這樣的應用場景下,似乎就是Martin所說的Transaction Script中的AnaemicDomainObject。
順便說一下,我個人是不同意Martin的RichDomainObject的說法。因爲,在數據應用系統中,Martin提到的相對於Transacton Script中AnaemicDomainObject的RichDomainModel往往沒有反映現實世界。一個Bank Account反映到現實世界,就是賬本中的一條記錄,它沒有自發的動作,譬如withdraw。它和Person不同,Person可以有say(String words)這樣的自發動作。Account的withdraw應該放到AccountManager中,由AccountManger來操作Account。不是說OO中的Object都需要有動作,現實世界中,本來就有靜態的、沒有自發動作的事物,譬如一個賬本、一個帳號、一個資料庫。雖然,不可否認,Rich Domain Model(對比AnaemicDomainObject說的)能夠帶來不少的好處(什麼樣的好處,你看看Martin的《Domain Logic and SQL》,就知道了)。 |
三、 我理解的Hibernate
本來,本文的題目叫做《EJB、Spring:剖析、批判和展望》,因爲我覺的Hibernate和EJB、Spring不是一個層次的東西,雖然,這個道理很淺顯,但是爲什麼那麼多人還拿Hibernate來攻擊EJB,來攻擊EntityBean?EntityBean是值得狠狠攻擊的,但是你用錯了槍。
我上面提到,支持CMP EntityBean的EJB Implements都有一個Persistence Engine,也就是O/R Mapping Engine。CMP O/R Mapping Engine用來做什麼的?它通過分析CMP Abastract Schema、分析EJBQL、分析Bean狀態等行爲,生成SQL,然後和DB 進行交互。
而在我眼裏,Hibernate不是”O/R Mapping Tool”這幾個字能概括的了。我說Hibernate是一款獨當一面的輕量級翻譯中間件,是Layer,和CMP EntityBean O/R Mapping Engine不是一個層次的東西了。
Application------->CMP EntityBean Operation-------->DB
|
(O/R Mapping Engine)
|---HQL、Criteria Query
Application------> Hibernate ------> |---POJO/PO Operation---------> DB
|---and so on
通過上面的兩個圖,你看出區別來了嗎?
EntityBean應用,不知道O/R Mapping Engine的存在,只需要使用EntityBean來完成交互。
而在Hibernate應用中,Application是直接使用Hibernate的。也就是說,它是直接使用O/R Mapping Engine的。
在這裏,我建議你停下來,想想EntityBean是不是應該對應Hibernate中的PO/POJO?舉個例子,你修改PO後,是不是需要sessionObj.update(po)來更新,這個sessionObj.update(po)是不是表示你直接使用Hibernate的Persitence Engine?是的。而在EntityBean中,你修改EntityBean後,你需要其它的行爲來使得EntityBean的變化同步到DB嗎?不需要。因爲,EJB Container攔截你的調用,在你更改Bean的field之前、之後,container會調用load/store方法的(當然,在BMP/CMP EntityBean中,情況是不同的,BMP EntityBean調用programmer自己用JDBC編寫的load/store等方法,而CMP EntityBean,使用CMP Peristence Engine來做這個工作)。這樣,就隱式的持久化數據了。不需要,你像Hibernate那樣調用session.update這樣的語句。EntityBean這種同步方式是對它性能差的重要原因之一。值得注意的是,EJB Implements對於EntityBean同步並不完全是我上面描述的那樣,同步的頻率和事務、特定的implements是緊密相關的。 |
總的來說,CMP EntityBean O/R Mapping Engine是爲靜態的、功能固定的EntityBean的O/R Mapping提供支持而開發的。而Hibernate擔任的是一個Layer的作用。
四、 Spring不是神話
Rd Johnson聰明在哪裏?聰明在,他堅持了自己的實踐,而不是隨大流。Rd Johnson認識到90%的應用不需要分佈式、不需要J2EE中那些重量級的技術,譬如JNDI,他就動手爲EJB脫去Remote這層皮、將大多數應用中不必要的技術隔離、改造。從適用範圍上來說,Spring對EJB做了90%的補充。
個人看法:Spring的哲學在於,framework針對最常見、最簡單的應用場景而設計,等到需要特殊技術的時候,再想辦法解決問題。這樣,在絕大多數沒有特殊要求的應用中,Spring就顯示出優勢來了。下面,我們會做詳細的講解。
4.1 Spring“無侵入性“是謊言,但是有資格笑”百步之外的EJB”
“無侵入性”是Spring標榜的特性。但是,我想說,Spring的“無侵入”是謊言,隨着應用的深入,“無侵入”對什麼framework來說,都是個神化。
什麼就叫“無侵入性”?部署到Spring中的Object不需要強制任何實現接口就可以說Spring是“無侵入性”的?我覺的,這是大錯特錯。如果你非要說,Spring的確不需要像EJB那樣強制實現一些接口,那麼我只能告訴你:
(1)Spring設想的Object的應用場景是從最簡單的出發。所以,它沒有,爲了一些預料中要使用的特性而強制Object實現一些特定的接口。但是,事實上,在Spring中,如果你的應用場景稍微深入一點,那麼你就和和Spring綁定了。
(2)Spring管理的Object,從某種意義上說是沒有狀態的。
針對第一點,我舉兩個個例子。(1)EJB內部方法的調用,會導致基礎設施不會起作用。但是Bean接口(SessionBean、EntityBean、MessageDrivenBean)中都有可以使Bean獲得自己Context的支持,譬如:SessionBean的setSessionContext(SessionContext ctx) 等等,容器部署Bean的時候會通過它給每個Bean設置一個上下文。而EJBContext中,有EJBObject getEJBObject這樣的函數,可以使得Bean獲得自身的EJBObject,這樣通過EJBObject來調用Bean自己的函數,基礎設施就會起作用了。而Spring中,如果,一個Object的函數需要調用自己的其它函數,而又希望譬如安全檢查、事務等等Aspect起作用?那麼Spring,怎麼做?你需要設置Bean的exposeProxy屬性。
ExposeProxy: whether the current proxy should be exposed in a ThreadLocal so that it can be accessed by the target. (It's available via the MethodInvocation without the need for a ThreadLocal.) If a target needs to obtain the proxy and exposeProxy is true, the target can use the AopContext.currentProxy() method. |
所以,當你需要上面說的內部調用需要基礎設施起作用的特性,不管在EJB還是Spring肯定需要和特定框架綁定的。爲什麼說,Spring五十步笑百步?因爲,我上面提到,Spring在Object很簡單的情況下,是可以任意部署的、複用的。而EJB卻不管你需不需要,開始就設想你需要的。同樣,Spring中的BeanFactoryAware、BeanNameAware等等接口也都說明了一點:Spring將特性從Object剝離,從而,儘量降低它的依賴性。只有當你的Object複雜的時候,framework纔會侵入你的Object。
針對,第二點,我想着重談一下。爲什麼說,從某種意義上說Spring中部署的對象是沒有狀態的?我們知道,Spring支持兩種Object:Singleton和Prototype。Spring Spec中,認爲,Singleton可以稱爲stateless的,Prototype可以稱爲是statefule的。而在EJB的世界中,StatefuleSessionBean和EntityBean也稱作是stateful的。那麼,它們的stateful分別意味着什麼?它們爲什麼在依賴性方面有那麼大的區別?爲什麼Spring中的Object不需要實現特定接口,而EJB需要?先來,看看EJB的SessionBean接口:
|
|
|
|
|
|
|
|
其中的setSessionContext我上面說過。看ejbActivate()、ejbPassive(),爲什麼會有這兩個函數?而Spring不需要實現有同樣函數的接口?這是EJB和Spring的對象管理機制的不同造成。EJB implements一般來說,爲了複用Bean,會採用一級Cache加上一級InstancePool(StatelessSessionBean是不需要Cache的),從而支持將StatefulSessionBean持久化到磁盤,支持EntityBean的Bean Instance(注意這個Bean Instance和client得到的EntityBean是不同的,它沒有和任何的DB Record關聯)的複用,這就導致了ejbAcrivate、ejbPassivate等的引入。但是,Spring沒有采用這樣管理機制,它只有Singleton/Prototype。而Prototype雖然也可以說成是Statefule的,但是它不會在不同的client中複用Object Instance,而是每一個client一個對象,哪怕一萬個client,那麼就產生一萬個Instance,而在EJB中,可能使用100 Instance來服務,將not active的Bean持久化到磁盤,複用Bean Instance。還請注意,這裏我不是說EJB中的StatefuleSessionBean好,事實上我發現,一般來說,當併發量很大時,採用節約內存而持久化Bean到磁盤這種策略,I/O瓶頸引起的問題更爲嚴重。
再看,ejbRemove,這個沒什麼多說的,Spring中你可以選擇實現InitializingBean、DisposableBean接口,但是Spring推薦不要這樣做,可以寫普通的init成員函數,然後在配置文件中指明init-method、destroy-method屬性,這樣避免和Spring框架的綁定。
總的來說,Spring從對象最基本的引用場景出發,當需要複雜特性的時候,纔會採用特殊機制來解決問題,也就是在這時,纔會使應用綁定到Spring中。所以,它的侵入性比較低,但是不是“無侵入性”,不是你想的那麼美好,當然,也沒有“絕對無侵入“的framework。
4.2 我覺的Spring IOC的設計思路不夠完美
Spring的IOC被一些人當作多麼神奇的東西。
EJB具有Spring中所說的那種IOC的能力嗎?答案是肯定的。EJB中的EJB引用、資源引用、環境屬性都可以說是IOC,不是嗎?然而,Spring和EJB的IOC不同在哪裏?
Spring注入的特色:主要考慮Local Object的查找,這個時候不需要任何的協議(譬如JNDI),當你需要注入Remote Object的時候,採用RMI協議或者使用第三方Tool(譬如Hessian)。
EJB的特色:無論你的Bean-Bean是否部署在同一臺機器上、Client->Bean是否在同一臺機器上,肯定需要通過JNDI來查詢Bean,只是,如果是它們在同一臺機器上的時候,你使用Local接口,這樣使得調用變爲Local調用,從而提升性能。EJB它從出生時起,就定位爲分佈式組件架構,一頭栽進“distributed”不容易出來了。這個可能就叫“尾大不掉”吧。
這一切的不同,只能說,它們的定位不同。一個更關注Local、一個更關注Remote。Spring仍然堅持它的哲學,從最基本的、大多數的場景考慮起,到特殊需要的時候,再想辦法來解決問題。它試圖找到J2EE開發和系統能力的均衡點。
可以說,Spring的做法,更加合情合理。但是,我也相信,Spring在”只是爲Remote注入提供簡單的支持“這一點上有點矯枉過正。我覺的,它可以做的更好,譬如通過作爲J2EE標準的JNDI來封裝Local、Remote查找。
目前,Spring不怎麼關心Remote Object注入,對於需要Remote注入的情況,只提供簡單的支持,而且還需要針對expert單獨寫配置信息。在這裏,EJB3.0的做法,我覺的,是目前,最方便、最理智、也是最有前途的。EJB3.0通過@remote、@local就可以讓EJB Server做不同的部署。
Spring導出遠程對象。
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- does not necessarily have to be the same name as the bean to be exported -->
<property name="serviceName"><value>AccountService</value></property>
<property name="service"><ref bean="accountService"/></property>
<property name="serviceInterface"><value>example.AccountService</value></property>
<!-- defaults to 1099 -->
<property name="registryPort"><value>1199</value></property>
</bean>
Spring中注入Remote是怎樣做的? <bean class="example.SimpleObject"> <property name="accountService"><ref bean="accountService"/></bean> </bean>
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <property name="serviceUrl"><value>rmi://HOST:1199/AccountService</value></property> <property name="serviceInterface"><value>example.AccountService</value></property> </bean> 看了,這段代碼,你就知道了。 這種方法非常的輕量級,從本質上來說,這種方法和JNDI沒有任何的不同,這種方法,也需要一個NamingService,還記得RMI編程中,運行服務端的時候,首先運行rmiregistry,這個實際上就是非常簡單的NamingService。看來,Rd Johnson對JNDI真是深惡痛絕啊。怎麼也不願意用JNDI。Spring這種方法,也許沒有JNDI那樣重量級,但是很顯然它不能支持工業級分佈系統,J2EE發展到今天,JNDI已經成爲最核心的技術,它不是簡單的Object Naming Service,它提供標準接口來定位用戶、微機、網絡、對象、服務器等等。它已經廣泛而深入的進入J2EE技術的各個領域、成爲J2EE系統的紐帶。 舉個很簡單的例子:我需要在Spring應用中動態獲取Remote Object,我該怎麼做?我需要無縫使用LDAP,我該怎麼做?答案是,Spring不能幫到你什麼。 那麼我就想,Spring爲什麼不使用JNDI來封裝Local、Remote查找兩種協議?從而,使用J2EE標準來實現無縫“注入”。這樣帶來的優點就是:(1)適應JNDI已經廣泛而深入滲透的領域(2)JNDI是J2EE標準(3)支持動態Remote Service獲取(4)支持工業級分佈系統。 Properties props = new Properties(); pros.put(“searchServiceFactory”,”org.net.spring.bean.BeanFactory”); Context ctx = new Context(env); ctx.lookup(“servicename”);
Properties props = new Properties(); pros.put(“searchServiceFactory”,”com.sun.j2ee.jndi”); Context ctx = new Context(env); ctx.lookup(“servicename”); |
從下面,你可以看出,我個人認爲Spring如果仍能OS,free,也許還會有很多人選擇它,但是如果,它試圖商業化,那麼可以肯定它必須向EJB3.0靠攏。向標準靠攏。 |
4.3 獨立的、完整的、面向Programmers的AOP框架纔是Spring真正的亮點
EJB具有Spring中所宣揚的AOP能力嗎?答案是肯定的。EJB的基礎設施就是使用AOP技術實現的。然而,我要說,EJB的AOP並沒有面向Programmers。爲什麼這麼說?EJB Implements爲你提供一組基礎設施(例如,JBOSS中那些可配置的interceptors)。你可以根據系統需要配置。但是,你不可以編寫自己的interceptor,然後配置到系統中。因爲EJB的interceptors往往和EJB Implements內部癒合很緊,你想編寫自己的interceptor,意味着你必須閱讀EJB Implements的source,瞭解它的實現。換句話說,EJB Implements中的AOP技術是爲了EJB Server能夠提供基礎設施而使用的,它沒有想到爲programmer提供更多的AOP能力。而Spring的AOP開始就是作爲一個框架來發展的,是獨立的、完整的。造成這種情況,是歷史的原因。EJB Implements作者也不是神人,他們不可能,N年前,就想到將AOP框架設計的足夠獨立,從而面向programmes。
個人觀點:EJB3.0在基礎設施方面的說明,基本沿襲EJB2.X的。只是,可以提到EJB3.0支持通過annotations來使用基礎設施,沒有說,EJB3.0需要完善的AOP框架,但是,我想,EJB3.0 Implments應該都會提供一個獨立的、完整的、面向programmer的AOP框架。事實上,JBOSS不早就有了J |
當然,在Spring中使用AOP也不是那麼的輕鬆,譬如,讓你自己寫TransactionProxy,你還是需要了解Spring AOP內部運作機制的。
4.4 Spring對其它框架的集成
這個問題,就不談了。
五、EJB將走向何方
5.1 EJB-3.0
我們沒必要說EJB2.X本身有多少的缺陷,畢竟,它是前一個J2EE時代的產物,只能說EJB2.X已經不能反映大多數J2EE應用的實際需要。過時了。那麼EJB3.0打算帶我們走向何方?
EJB3.0 Spec除了針對簡化開發、方便測試、方便部署等目標做了不少的修改,更重要的是EJB3.0對SessionBean,特別是EntityBean模型做了一個全面的整容手術。這種修改是革命性的。
在我的《如果我來改進EJB2.X模型》中,我談到,如果,讓我對EJB2.X的EntityBean模型做修改,那麼首先需要爲新的模型定好位。就拿EntityBean來說好了。
第一條路:繼續EntityBean設計的初試理念:Remote Domain Model(包括BMP EntityBean代表的Domain Model和CMP EntityBean代表的AnaemicDomainObject),並且保留Local接口,力圖改經持久模型的設計,提高性能(即使CMP EntityBean的性能也是難以令人接受的,這種情況,我個人認爲,主要是因爲EntityBean模型設計的不好,在我的另一篇《如果我來改進EJB2.X模型》中有深入的分析)、增強功能(EJBQL實在太弱),讓那些連SessionBean、EntityBean都需要部署在不同Server上的應用來爲EJB2.X的EntityBean留口氣。
但是,顯然,EJB Server提供商是不可能甘心這一點羹的,因爲那樣的應用實在太少了。事實已經證明,如果EntityBean的Remote不是必須的,那麼RemoteEntityBean性能上是不可行的,它只能工作在SessionBean後端,然而,即使EntityBean工作在SessionBean後端,但是EntityBean本身的侷限性也太多,粒度要麼太粗要麼太細,性能、功能太弱,等等,開發數據應用非常地蹩腳,那麼如果,在Remote EntityBean不是必須的情況下,我爲什麼不完全放棄EntityBean,在SessionBean後端使用其它的O/R Mapping Tool來開發數據應用,譬如Hibernate。這就是,EntityBean可以走第二條路。當然,從某種意義上來說,也是它必須走的路。
第二條路:完全拋棄EntityBean,採用Hibernate這樣的O/R Mapping Engine作爲Session Bean、Message-Driven Bean的後端數據持久化工具。而從EJB3.0可以看出,EJB3.0的確完全拋棄了傳統的EntityBean模型。個人意見:可以這樣說吧,EntityBean已經不復存在,Expert Group在SessionBean下給你換上了一個非常sharp的Persistence engine,你拿着engine,想幹什麼就幹什麼好了(上面講過,EntityBean中,PersitenceEngine對client是通明的,這是由這兩種引擎的本質作用決定的。有人說,EntityBean Application中不可以使用Dynamic Query,只能在配置文件中申明EJBQL,這些都是兩種Persistence Engine的本質所決定的)。蹩腳的、強制模型的EntityBean不復存在!另外,EntityBean Remote特性在EJB3.0中根本沒有提到,或許只是作爲一個可選特性了吧(我還沒有想到,EJB3.0中,如何來支持Remote PO,這個問題很詭異)。看來,Expert Group已經徹底否定了EntityBean的設計,或者說EntityBean的確是不符合實際需求的,Remote EntityBean、Remote Domain Object在絕大多數情況下是不切實際的。
話外題:Hibernate和JDO的關係,很微妙。EJB3.0和JDO的合併、Gavin進入EJB3.0 ExpertGroup令人很迷惑。EJB3.0的持久化模型採用JDO,應該是理所當然的。但是,目前,EJB3.0的Persitence Engine部分似乎被Hibernate左右,那麼JDO的位置應該在哪裏? |
六、Spring將走向何方
無疑,“聽起來很美妙的”IOC、實力、實用派Spring AOP、集成大量framework的Spring是目前、對分佈式、高級J2EE特性要求不強的系統的最合理選擇。但是,你可以看到,Spring能做到的,除了集成大量framework這個特性外(當然這個永遠不會被寫進EJB Spec,但是如果EJB Server供應商想這樣做,也是非常簡單的事),EJB3.0也能做到,而且很多地方做的比Spring好很多,最重要的,EJB是標準,所以,很肯定的說,如果Spring OS、free,保持目前的姿態發展,仍然會成爲開發人員不錯的選擇,然而,如果Spring試圖商業化,我是Rd Johnson的話,我會向EJB3.0靠攏,搖身成爲EJB3.0 Server提供商。
七、EJB3.0是J2EE商用framework的未來
大肆革新過的EJB3.0,是J2EE商用framework的將來。