如果你選擇使用EJB,Spring能在EJB實現(xiàn)和客戶端訪問EJB兩方面都提供很大的好處。
對業(yè)務(wù)邏輯進行重構(gòu),把它從EJB facades中取出到POJO已經(jīng)得到了廣泛的認同。(不講別的,這使得業(yè)務(wù)邏輯更容易單元測試,因為EJB嚴重依賴于容器而難于分離測試。)Spring為session bean和message driver bean提供了方便的超類,使得通過自動載入基于包含在EJB Jar文件中的XML文檔BeanFactory讓這變得很容易。
這意味著stateless session EJB可以這么獲得和使用所需對象:
代碼: |
import org.springframework.ejb.support.AbstractStatelessSessionBean; public class MyEJB extends AbstractStatelessSessionBean implements MyBusinessInterface { private MyPOJO myPOJO; protected void onEjbCreate() { this.myPOJO = getBeanFactory().getBean("myPOJO"); } public void myBusinessMethod() { this.myPOJO.invokeMethod(); } } |
假定MyPOJO是一個接口,它的實現(xiàn)類——以及任何它需要的配置,注入基本的屬性和更多的合作者——在XML bean factory 定義中隱藏。
我們通過在ejb-jar.xmldeployment descriptor中名為ejb/BeanFactoryPath的環(huán)境變量定義告訴Spring去哪兒裝載XML文檔。如下:
代碼: |
<session> <ejb-name>myComponent</ejb-name> <local-home>com.test.ejb.myEjbBeanLocalHome</local-home> <local>com.mycom.MyComponentLocal</local> <ejb-class>com.mycom.MyComponentEJB</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <env-entry> <env-entry-name>ejb/BeanFactoryPath</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>/myComponent-ejb-beans.xml</env-entry-value></env-entry> </env-entry> </session> |
myComponent-ejb-beans.xml 文件將會從classpath裝載:在本例中,是EJB Jar文件的根目錄。每個EJB都能指定自己的XML文檔,因而這個機制能在每個EJB Jar文件中使用多次。
Spring 的超類實現(xiàn)了EJB中諸如setSessionContext()和ejbCreate()的生命周期管理的方法,讓應(yīng)用程序開發(fā)者只需選擇是否實現(xiàn)Spring的onEjbCreate()方法。
使用EJB
Spring還讓實現(xiàn)EJB變得更加容易。許多EJB程序使用Service Locator和Business Delegate模式。這些比在客戶代碼中遍布JNDI查找強多了,但是它們常見的實現(xiàn)方式有顯著的缺點,例如:
基于這些和其他原因,傳統(tǒng)的EJB訪問,如在Sun Adventure Builder和OTN J2EE Virtual Shopping Mall中展示的那樣,會降低生產(chǎn)率并且?guī)盹@著的復(fù)雜度。
Spring通過引入codeless business delegate前進了一步。有了Spring,你不再需要再編寫另一個Service Locator,另一個JNDI查找,或者在硬編碼的Business Delegate中重復(fù)代碼,除非你肯定這增加了價值。
例如,假定我們有使用local EJB的web controller。我們將遵循最佳實踐,使用EJB Business Methods Interface模式,EJB的local interface extend非EJB專有的業(yè)務(wù)方法接口。(這么做的主要的一個原因是確保在本地接口和bean實現(xiàn)類中方法簽名的自動同步。)讓我們調(diào)用這個業(yè)務(wù)方法接口MyComponent。當(dāng)然我們還需要實現(xiàn)local home接口并且提供實現(xiàn)SessionBean和MyComponent業(yè)務(wù)方法的bean的實現(xiàn)類。
用了Spring EJB 訪問,我們把我們的web層controller和EJB實現(xiàn)掛接上所需要進行的Java編碼僅僅是在我們的controller中暴露一個類型MyComponent的setter方法。這將如下保存作為實例變量的引用:
我們隨后在任何業(yè)務(wù)方法中使用這個實例變量。
Spring自動完稱剩下的工作,通過像這樣的XML bean定義。LocalStatelessSessionProxyFactoryBean是一個可以用于任何EJB的通用factory bean。它創(chuàng)建的對象能夠自動被Spring轉(zhuǎn)型為MyComponent類型。
在幕后有許多魔法般的事情發(fā)生,Spring AOP framework的殷勤,雖然不強迫你使用AOP的概念享受這些結(jié)果。“myComponent”bean定義為EJB創(chuàng)建一個代理,它實現(xiàn)了業(yè)務(wù)方法的接口。EJB local home在啟動的時候被緩存,因而只需要一次JNDI查找。每次EJB被調(diào)用的時候,代理調(diào)用local EJB中的create()方法并且調(diào)用EJB中對應(yīng)的業(yè)務(wù)方法。
myController bean定義為這個代理設(shè)置controller類的myController屬性。
這個EJB訪問機制極大簡化了應(yīng)用程序的代碼:
在實際程序中的性能測試和經(jīng)驗標(biāo)明這種方法(包括對目標(biāo)EJB的反射調(diào)用)的性能影響是很小的,在典型的應(yīng)用中檢測不出。記住無論如何我們都不希望使用fine-grained的EJB調(diào)用,因為會有有關(guān)應(yīng)用服務(wù)器上的EJB的底層架構(gòu)方面的代價。
我們可以把相同方法應(yīng)用于遠程EJB,通過類似org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean的方法。然而我們無法隱藏遠程EJB的業(yè)務(wù)方法接口中的RemoteException。
Spring還讓實現(xiàn)EJB變得更加容易。許多EJB程序使用Service Locator和Business Delegate模式。這些比在客戶代碼中遍布JNDI查找強多了,但是它們常見的實現(xiàn)方式有顯著的缺點,例如:
使用EJB的典型代碼依賴Service Locator或者Business Delegate singletons,使得測試難于進行。
在Service Locator模式?jīng)]有使用Business Delegate的情況下,程序代碼還要在EJB home中調(diào)用create()方法,并且處理可能導(dǎo)致的異常。因而仍然綁定在EJB API身上,忍受著EJB 編程模型的復(fù)雜度。
實現(xiàn)Business Delegate模式通常導(dǎo)致顯著的代碼重復(fù),其中我們必須編寫大量僅僅是調(diào)用EJB同等方法的方法。
基于這些和其他原因,傳統(tǒng)的EJB訪問,如在Sun Adventure Builder和OTN J2EE Virtual Shopping Mall中展示的那樣,會降低生產(chǎn)率并且?guī)盹@著的復(fù)雜度。
Spring通過引入codeless business delegate前進了一步。有了Spring,你不再需要再編寫另一個Service Locator,另一個JNDI查找,或者在硬編碼的Business Delegate中重復(fù)代碼,除非你肯定這增加了價值。
例如,假定我們有使用local EJB的web controller。我們將遵循最佳實踐,使用EJB Business Methods Interface模式,EJB的local interface extend非EJB專有的業(yè)務(wù)方法接口。(這么做的主要的一個原因是確保在本地接口和bean實現(xiàn)類中方法簽名的自動同步。)讓我們調(diào)用這個業(yè)務(wù)方法接口MyComponent。當(dāng)然我們還需要實現(xiàn)local home接口并且提供實現(xiàn)SessionBean和MyComponent業(yè)務(wù)方法的bean的實現(xiàn)類。
用了Spring EJB 訪問,我們把我們的web層controller和EJB實現(xiàn)掛接上所需要進行的Java編碼僅僅是在我們的controller中暴露一個類型MyComponent的setter方法。這將如下保存作為實例變量的引用:
代碼: |
private MyComponent myComponent; public void setMyComponent(MyComponent myComponent) { this.myComponent = myComponent; } |
我們隨后在任何業(yè)務(wù)方法中使用這個實例變量。
Spring自動完稱剩下的工作,通過像這樣的XML bean定義。LocalStatelessSessionProxyFactoryBean是一個可以用于任何EJB的通用factory bean。它創(chuàng)建的對象能夠自動被Spring轉(zhuǎn)型為MyComponent類型。
代碼: |
<bean id="myComponent" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"> <property name="jndiName"><value>myComponent</value></property> <property name="businessInterface"><value>com.mycom.MyComponent</value></property> </bean> <bean id="myController" class = "com.mycom.myController" > <property name="myComponent"><ref bean="myComponent"/></property> </bean> |
在幕后有許多魔法般的事情發(fā)生,Spring AOP framework的殷勤,雖然不強迫你使用AOP的概念享受這些結(jié)果。“myComponent”bean定義為EJB創(chuàng)建一個代理,它實現(xiàn)了業(yè)務(wù)方法的接口。EJB local home在啟動的時候被緩存,因而只需要一次JNDI查找。每次EJB被調(diào)用的時候,代理調(diào)用local EJB中的create()方法并且調(diào)用EJB中對應(yīng)的業(yè)務(wù)方法。
myController bean定義為這個代理設(shè)置controller類的myController屬性。
這個EJB訪問機制極大簡化了應(yīng)用程序的代碼:
Web層的代碼不依賴于EJB的使用。如果你想要使用POJO,mock object或者其他test stub替代EJB引用,我們可以簡單地改動一下myComponent bean定義而不影響一行Java代碼
我們還不需要寫一行JNDI查找或者其他EJB plumbing code。
在實際程序中的性能測試和經(jīng)驗標(biāo)明這種方法(包括對目標(biāo)EJB的反射調(diào)用)的性能影響是很小的,在典型的應(yīng)用中檢測不出。記住無論如何我們都不希望使用fine-grained的EJB調(diào)用,因為會有有關(guān)應(yīng)用服務(wù)器上的EJB的底層架構(gòu)方面的代價。
我們可以把相同方法應(yīng)用于遠程EJB,通過類似org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean的方法。然而我們無法隱藏遠程EJB的業(yè)務(wù)方法接口中的RemoteException。