1. 程式人生 > >《Spring 5 官方文件》整合EJB

《Spring 5 官方文件》整合EJB

原文連結

25.1 介紹

作為一個輕量級的容器,Spring通常被認為是EJB的替代品。我們相信對域大多數就算不是最多的應用和用例來說,Spring作為一個容器結合其豐富的在事物,ORM和JDBC訪問方面的支援功能,是比通過一個EJB容器和EJBs來實現同等的功能更好的選擇。

然後,需要注意的是使用Spring並不會阻止你使用EJBs。實際上,Spring使訪問EJBs,實現EJBs以及其中的功能更加容易。另外的,使用Spring來獲取EJBs提供的服務可以允許這些服務透明的在本地EJB,遠端EJB或者POJO(Plain Old Java Object)變數中切換,而不需要客戶端程式碼做改變。

在這一章,我們會看到Spring使如何幫助你獲取和實現EJBs。Spring提供特定的值在訪問無狀態會話元件(SLSBs),所以我們從這裡開始討論。

25.2 獲取EJBs

25.2.1 概念

呼叫一個本地或者遠端的無狀態會話元件中的方法,客戶端程式碼必須正常執行JNDI查詢來獲取本地或者遠端的EJB Home Object,然後使用’create’ 方法來獲取一個真正的(本地或者遠端的)EJB物件。然後一個或者多個方法會在EJB中被呼叫。
為了避免重複底層級的程式碼,很多EJB應用使用服務定位器和業務代表模式。它們比起在客戶端程式碼中到處使用JNDI lookup要好,但是也有很明顯不好的地方,比如:

  • 通常使用EJBs的程式碼依賴於服務定位器或者業務代理單例,所以很難測試。
  • 在使用了服務定位器模式而不是用一個業務代理的情況下,應用程式碼仍然需要結束時在一個EJB home上呼叫create()方法,並且處理結果中的異常。所以它仍然和EJB的API綁在一起,還會有EJB程式設計模型的複雜性。
  • 實現業務代理模式常常會帶來明顯的程式碼重複,我們不得不寫很多的方法只是簡單的呼叫EJB中相同的方法。

25.2.2 訪問本地無狀態會話元件(SLSBs)

假設我們有一個web容器需要使用到本地的EJB。我們將會遵循最好的實踐並且使用EJB業務方法介面模式,所以就是EJB的本地介面擴充套件一個不是EJB-specific的業務方法介面。讓我們叫這個介面為MyComponent

public interface MyComponent {
    ...
}

一個主要使用業務方法介面模式的原因是為了確保本地介面中的方法簽名和bean實現類同步是自動的。另外的一個原因是它讓我們可以在需要的時候更加容易轉換為POJO來實現這個服務。當然我們也會需要去實現本地的home介面並且提供一個實現類來實現SeesionBeanMyComponent業務方法介面。現在我們唯一需要的Java編碼是暴露一個控制器中setter方法進行設定MyComponent從而將我們的web層控制器連線到這個EJB實現。這將會在容器中儲存引用作為一個例項變數:

private MyComponent myComponent;

public void setMyComponent(MyComponent myComponent) {
    this.myComponent = myComponent;
}

我們之後可以使用這個例項變數在這個控制器中的任何一個業務方法中。現在假設我們在一個Spring控制器以外獲取了我們的控制器物件,我們可以在(相同上下文中)配置一個LocalStatelessSessionProxyFactoryBean例項,這是一個EJB代理物件。這個代理的配置以及控制器的myComponet屬性值是在一個配置項完成的,如下:

<bean id="myComponent"
        class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="ejb/myBean"/>
    <property name="businessInterface" value="com.mycom.MyComponent"/>
</bean>

<bean id="myController" class="com.mycom.myController">
    <property name="myComponent" ref="myComponent"/>
</bean>

這個配置的生效背後Spring AOP框架做了很多的工作,儘管你並不強制性需要使用AOP觀念就能享受到這個結果了。這個myComponetbean定義了一個EJB的代理實現了業務介面。這個EJB local home在啟動的時候會被快取,所以這隻有一個單獨的JNDI lookup。每當這個EJB被呼叫的時候,這個代理就會呼叫本地EJB的classname方法並且呼叫EJB上相應的業務方法。
myControllerbean定義為這個EJB代理設定了控制類的myComponet的屬性。
可選的(最好是在許多這樣代理定義的情況下),考慮使用<jee:local-slsb>在Spring”jee”名稱空間中配置元素:

<jee:local-slsb id="myComponent" jndi-name="ejb/myBean"
        business-interface="com.mycom.MyComponent"/>

<bean id="myController" class="com.mycom.myController">
    <property name="myComponent" ref="myComponent"/>
</bean>

這種EJB獲取機制極大的簡化了應用程式碼:web層的程式碼(或者其他EJB客戶端程式碼)對使用EJB沒有依賴。如果我們想要使用一個POJO或者一個模擬物件獲取其他的測試樁來替代這個EJB的引用,我們可以簡單的修改這個myComponetbean的定義而不需要修改任何Java程式碼。另外的,我們也不需要在我們應用中寫任何一行JNDI lookup或者其他EJB樣板程式碼。
Benchmarks和其他在實際應用中的經驗表明這種方法(涉及目標EJB的反射呼叫)的效能開銷是很小的,通常在使用中是不可檢測到的。記住我們並不想要細密度的匯出呼叫EJBs,因為在應用伺服器中有使用EJB基礎架構的開銷。
這裡有一個關於JNDI lookup的警告。在一個bean容器中,類最好作為一個單例來使用(似乎沒有道理把它作為原型使用).然而,如果那個bean容器預先例項化了單例你也許會遇到問題如果這個Bean容器在EJB容器載入目標EJB之前載入了。因為這個JNDI lookup會在這個類的init()方法中呼叫並且被快取,但是這個EJB還沒有繫結到對應的目標地址。這個解決的方法是不要預先例項化這個工程物件,而是在第一次使用的時候再建立它。在XML容器中,它被lazy-init屬性控制。

儘管這不會是大多是大多數Spring使用者的興趣所在,那些使用EJB做AOP程式設計的使用者也許想要看看LocalSlsbInvokerInterceptor

25.2.3 獲取遠端的無狀態會話元件(SLSBs)

獲取遠端EJBs基本上和獲取本地EJBs相同,除了SimpleRemoteStatelessSessionProxyFactoryBean或者<jee:remote-slsb>配置元素會被用到。當然不論用不用到Spring,遠端呼叫語義應用;一個呼叫在其他VM其他電腦上的物件的方法有時需要被不同的對待根據應用場景和失敗的控制代碼。
Spring的EJB客戶端再一次比非Spring方法更加有優勢。通常EJB客戶端程式碼要輕鬆的在呼叫EJBs本地和遠端之間切換是有問題的。因為遠端的介面方法必須宣告它們可能丟擲RemoteException,而客戶端程式碼必須處理它,而本地的介面沒有這個異常。客戶端處理本地EJBs的程式碼如果要改為處理遠端EJBs通常需要修改增加處理遠端異常,並且客戶端如果本來是處理遠端EJBs的需要改為處理本地EJBs的程式碼可以保持不變但是做了很多不必要的處理遠端異常的事情,或者就需要移除這部分程式碼。使用Spring 遠端EJB代理你可以不用宣告任何丟擲RemoteException的業務方法介面和實現EJB程式碼,這裡有一個遠端介面除了丟擲異常以外完全相同,而是依靠代理來動態的處理這兩個介面使它們表現的一樣。這樣客戶端程式碼無需處理
RemoteException類。任何在呼叫這個EJB過程中丟擲的RemoteException將會被再次丟擲作為沒有沒檢查的RemoteAccessException類,這個類是RuntimeException的子類。這個目標服務可以在不需要客戶端程式碼知曉的情況下在本地EJB或者遠端EJB(或者甚至是簡單的Java物件)實現中切換。當然,這個是可選擇的;你也可以在你的業務介面中宣告RemoteException

25.2.4 獲取EJB 2.x SLSBs和EJB 3 SLSBs的比較

通過Spring獲取EJB 2.x會話元件和EJB3會話元件基本上是透明的。Spring的EJB獲取器包含了<jee:local-slsb><jee:remote-slsb>,會在執行時透明的採用實際的元件。它們如果找到了home interface(EJB 2.x 風格)就會獲取,反之就直接執行元件呼叫(EJB 3風格)。
Note:對於EJB 3會話元件,你也可以直接的使用JndiObjectFactoryBeanjee:jndi-lookup因為完全有效的元件引用在這裡被暴露給了JNDI loolups。定義顯示的<jee:local-slsb><jee:remote-slsb>lookups只是提供了一致和更加顯示的EJB訪問配置。