程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 在J2EE 1.3中消除服務定位器實現中的緩存

在J2EE 1.3中消除服務定位器實現中的緩存

編輯:關於JAVA

引言

Service Locator 是 Java™ 2 Enterprise Edition (J2EE) 應用程序中一個比 較流行的應用程序設計模式。這個模式通過目錄服務封裝訪問組件的代碼,如 JNDI 客戶 端代碼之類,因此客戶端可以簡單的以資源名通過驗證並返回這個資源。服務定位器實現 通常包括資源緩存,以此來避免對相同資源的重復查找。然而這只能在 J2EE 1.2 中正常 工作,但在 J2EE 1.3 和以後的版本中,緩存可以在應用程序部署中引入微妙且難以診斷 的錯誤。因此,在 J2EE 1.3 應用程序中,服務定位器的實現不應該包含資源緩存。

JNDI 目錄服務

Java Naming and Directory Interface(JNDI)是 J2EE 平台的一部分,它使得 Java 程序可以通過唯一的名稱來訪問資源,而並不用考慮資源是在何處存儲的、它是如 何實現的、容器以及它的 JNDI 提供者是如何實際訪問資源的;資源可以是任何程序需要 全局訪問的對象。

我們將主要回顧 JNDI 是如何工作的,首先要弄清楚影響 Service Locator 模式的部 分。要了解更多,請參見 Sun 的 JNDI 指南(參見 參考資料)。

JNDI 上下文

JNDI 名是以層次樹結構排列的,就像文件系統的目錄結構或一系列 Java 類的包結構 。在 J2EE 中有對資源的通用類型的 JNDI 資源環境引用子上下文名的約定。表 1 顯示 了典型的 JNDI 子上下文和類型。

表 1 子上下文 Java 接口 描述 ejb javax.ejb.EJBHomejavax.ejb.EJBLocalHome An Enterprise JavaBean (EJB) home jdbc javax.sql.DataSource A Java Database Connectivity (JDBC) 數據源 jms javax.jms.ConnectionFactoryjavax.jms.Destination Java Messaging Service (JMS) 連接工廠或接收站 eis javax.resource.cci.ConnectionFactory J2EE Connector 連接工廠 mail javax.mail.Session JavaMail 會話 url java.net.URL Web 服務連接工廠

每個子上下文名被用作 JNDI 表達式的一部分,以此來訪問客戶端本地上下文中的對 象。例如, java:comp/env/ejb 提供對 EJB 本地接口的訪問,而 java:comp/env/jdbc 提供對 JDBC 數據源的訪問。

為何使用 JNDI?

正如大部分 J2EE 服務一樣,JNDI 只提供了標准接口(在 javax.naming 包中定義) 而沒有具體實現。作為透明性 JNDI 提供的例子之一,設想一個 JMS 應用程序提供者( 例如 WebSphere® MQ),它是由一組受控對象來配置的(連接工廠和接收站),而組 件應用程序將使用這些對象處理消息。提供者將組織這些資源並將其安排在某個目錄中( 或有可能在許多目錄中,例如每個受控對象類型對應一個目錄),這就使得應用程序通過 它的名稱訪問組件。這個目錄結構可能是廠商指定的,但因為提供者實現了 JMS 規范, 所以提供者也必須確保能夠通過 JNDI API 得到它的資源,而不用管實際的目錄是如何實 現的。JNDI 提供對資源的一致訪問,而這些資源不一定是一致的。

而且,展示於應用程序面前的是一個單獨的個體,連續的 JNDI 樹可以(通常是 “肯定”)將幾個不同的提供者的資源集合在一起。JNDI 將這個細節從應用 程序中隱藏了起來,因此代碼只需知道資源的名稱,而不用知道諸如提供者所包含資源的 內容、如何對提供者進行訪問、提供者為資源使用什麼樣的名稱等等。

J2EE 應用程序服務器(例如 WebSphere Application Server)實現其自身的 JNDI 目錄,服務器的應用程序正是使用它來訪問所有資源。應用程序服務器需要構造 JDBC、 JMS 和其他資源,使其適用於服務器的應用程序。因為提供者通過 JNDI 提供他們的資源 ,所以應用程序服務器的 JNDI 樹可以輕易地包含資源節點。

圖 1. 應用程序服務器 JNDI 樹

EJB 組件甚至並不知道其中的一些資源是遠程的並且是在 EJB 容器之外被管理的。它 通過相同的 JNDI 樹以相同的方式訪問所有的資源。這是組件重用的很重要的部分:組件 訪問資源而不用確切的了解資源是如何被提供的能力。

如何使用 JNDI

使用 JNDI,代碼可以訪問其組件的 JNDI 上下文並且使用它執行 查看,這是 JNDI 的一項操作,該操作接收一個名稱並返回該名稱的一個對象的綁定,這很像是 java.util.Map 對象中的 get(Object) 方法。

既然 JNDI 可以被用來將任何類型的對象綁定到一個名稱,J2EE 就主要適用 JNDI 來 提供對 資源的訪問。通過 JNDI 查找訪問資源,組件以 資源名方式傳遞,然後返回在部 署描述符中由 資源引用元素所描述的資源。資源的大部分類型實際上都是 資源工廠,容 器以此來控制實例的產生、緩沖池的組合以及共享資源連接。資源工廠類型的例子包括: javax.ejb.EJBHome、 javax.ejb.EJBLocalHome、 javax.sql.DataSource 以及 javax.jms.ConnectionFactory。

舉例來說,下面的代碼片斷使用 JNDI 來查找資源名 jdbc/datasource,它返回一個 資源工廠: DataSource 對象。

javax.naming.Context root = new javax.naming.InitialContext();
javax.sql.DataSource datasource = (javax.sql.DataSource) root.lookup("java:comp/env/jdbc/datasource");

以此方式,代碼可以脫離容器的資源管理而仍然可以輕易的通過資源的名稱訪問資源 。代碼與容器之間只需在名稱的使用上達成一致就可以。

資源名映射

資源實際上是有兩個互相分離但又在 J2EE 中互有關聯的名稱:

資源引用名:資源引用的名稱,是代碼部分用來識別資源之用。

JNDI 名:容器用來識別資源的名稱。

有了這種安排,代碼和容器甚至在資源所使用的名稱上不用達成一致。當代碼被部署 時,部署者(J2EE 管理員,在容器中安裝應用程序組件的那個人)使用部署工具將每個 資源引用名都映射到與它對應的 JNDI 名稱上。

舉例來說,代碼可能要引用一個叫 jdbc/AccountDB 的數據庫。此時,賬戶數據可能 存儲在 Cloudscape® 數據庫中,這樣容器所引用的就是 jdbc/Cloudscape。部署者 將資源引用名 jdbc/AccountDB 綁定到 JNDI 名 jdbc/Cloudscape:

表 2 EJB 名 應用程序資源名 容器資源名 任何 EJB 組件 jdbc/AccountDB jdbc/Cloudscape

然後在實際運作中,當代碼訪問 AccountDB 時,它將得到 Cloudscape 數據庫。

應用程序資源名轄區

在 J2EE 1.2 中,資源名都是全局的,因此當兩個組件引用相同資源名時,他們得到 相同的資源引用,這是因為一個資源名映射到一個資源。在 J2EE 1.3 中開始,每個組件 (也就是說,每個 EJB bean 類和每個 Web 應用程序)定義其自身的一套資源名,在這 裡每個組件的資源名與容器資源名互相獨立。

這個改變對 J2EE 1.3 規范的影響是在其中加上了單獨的一段,在 J2EE.5.4.1.2 "在部署描述符中聲明資源管理器連接工廠引用。" 部分,其中闡述道:

其它的應用程序組件可以使用相同的資源引用名而不沖突(參見 參考資料中的 J2EE 1.2 和 1.3 規范)。

這意味著兩個組件可以指向相同的資源名,而在實際運作當中卻被綁定去訪問兩個不 同的容器資源。可以認為這是 重載資源名。在兩個不同的組件中相同的資源名實際上是 映射到兩個不同的容器資源。因此,要了解重載名是引用哪個資源,兩組件中的一個也必 須了解使用重載名的那個組件,並且知道那個組件是如何被映射到容器名的。

重載名舉例

例如,應用程序可以包含兩個會話 bean:

EmployeeBean:允許用戶(公司雇員)訪問多個客戶的賬戶及其他有關賬戶的機密信 息。

CustomerBean: 允許用戶(客戶)訪問他們個人賬戶。

簡單的說,兩個 bean 都可以使用資源名 jdbc/AccountDB,這是因為他們都使用客戶 賬戶數據庫。管理員為訪問數據庫會定義一個數據源,稱為 jdbc/AccountDS,然後在部 署時期將兩個組件中的 jdbc/AccountDB 綁定到 jdbc/AccountDS。

表 3 EJB 名 應用程序資源名 容器資源名 EmployeeBean jdbc/AccountDB jdbc/AccountDS CustomerBean jdbc/AccountDB jdbc/AccountDS

此時,資源名 jdbc/AccountDB 還沒有重載,因為它總是映射到相同的容器資源。資 源名是共享的,因為它被多個組件所使用,但是它沒有被重載。

現在,讓我們討論一下已經需要我們來考慮的管理問題。客戶可以訪問並甚至能改變 在賬戶數據庫中的機密數據,而這些數據應當只能由公司的雇員來更改。業務需求指出, 特定的數據庫表應當只能由雇員來操作而不是被顧客操作。為了實施這一點,數據庫管理 員創建兩個分離的登錄,一個是雇員使用的,一個是顧客使用的。顧客登錄不能訪問存儲 機密數據的表,而只有對特定表的只讀權限;雇員登錄擁有全部的訪問權限。通過這種方 式,即使 Java 應用程序有錯誤發生,允許顧客訪問這些表,數據庫也會阻止顧客的這種 訪問。

為支持這個新的需求,使用這些新的數據庫登錄,部署者在容器中配置兩個分離的數 據源,分別對應兩個登錄。這兩個數據源分別稱作: jdbc/AccountEmployeeDS 和 jdbc/AccountCustomerDS。在部署時期,部署者分別對應的綁定資源名:

表 4 EJB 名 應用程序資源名 容器資源名 EmployeeBean jdbc/AccountDB jdbc/AccountEmployeeDS CustomerBean jdbc/AccountDB jdbc/AccountCustomerDS

應用程序資源名 jdbc/AccountDB 現在已經是重載的名稱了。它所引用的資源,雇員 訪問的數據源或顧客訪問的數據源,要使用哪個數據源取決於是哪個組件在使用資源名。

以這種方式,部署者可以改變應用程序的選項來滿足新的需求而不用改變代碼,他只 需改變容器配置及改變現有代碼的部署。

同樣地,這種重載資源映射可以用來使不同組件使用幾個不同類型的資源:

不同的數據庫,可能包含各自的一套數據,可能在不同的提供商處運行為產品。

不同的 JMS 消息系統,對業務模式來說可能是一個內部的也可能是一個外部的,可能 在不同的提供商處運行為產品。

相同 Web 服務的多次部署,可能提供不同質量的服務。

重載是 J2EE 1.3 應用程序資源名的一種能力,因為他們是組件范圍的。J2EE 1.2 的 全局應用程序資源名是沒有這種能力的。

Service Locator 模式

Service Locator 模式(參見 參考資料)是 J2EE 應用程序中著名的設計模式,它封 裝了需要通過目錄服務(如 JNDI)查找資源的代碼。業務邏輯代碼使用服務定位器避免 目錄查找代碼變得混亂,因此它很容易理解。客戶端為資源傳遞一個唯一的標識符(資源 名),服務定位器找到這個資源並把它返回給客戶端。服務定位器封裝 JNDI 上下文的訪 問、縮小並轉化對相應 Java 接口的引用以及處理錯誤。

例如,允許客戶端訪問數據源的服務定位器代碼表面上與此相似:

private InitialContext initialContext;

    private ServiceLocator()
    throws ... {

    try {

    initialContext =
    new InitialContext();

  }

    catch ...

}

    public DataSource getDataSource(String dataSourceName)
    throws ... {

  DataSource dataSource =
    null;

    try {

    dataSource = (DataSource) initialContext.lookup (dataSourceName);

  }

    catch ...

    return dataSource;

}

服務定位器通常作為集合來實現,因此整個應用程序(特別地,所有的代碼運行在相 同的 Java 虛擬機(JVM)上)使用相同的實例。(如果服務定位器類是 JAR 通用性的一 部分並被多個應用程序共享,這些應用程序將共享相同的實例。)由於服務定位器基本上 是無狀態的,所以多個組件共享相同實例是沒有問題的。

服務定位器的實現通常包括資源緩存,因此當應用程序重復訪問相同的資源時,服務 定位器只是在首次訪問的時候執行查找。當資源查找代價很高時,緩存通過避免重復查找 相同資源而提高了效率。這在服務定位器也是一個集合的時候很受用,因此從一個組件查 找資源時也幫助了那個組件實例,其他相同組件類型的實例以及其他避免重復查找的組件 類型實例。

包含訪問數據源緩存的服務定位器實現如下:

private InitialContext
initialContext;

    private Map cache;

    private ServiceLocator()
    throws ... {

    try {

    initialContext =
    new InitialContext();

    cache = Collections.synchronizedMap(
    new HashMap());

  }

    catch ...

}

    public DataSource getDataSource(String dataSourceName)
    throws ... {

  DataSource dataSource =
    null;

    try {

    if (cache.containsKey(dataSourceName)) {

      dataSource = (DataSource) cache.get (dataSourceName);

    }

    else {

      dataSource = (DataSource)
initialContext.lookup(dataSourceName);

      cache.put(dataSourceName, dataSource);

    }

  }

    catch ...

    return dataSource;

}

服務定位器緩存錯誤

當在 J2EE 1.2 中首次使用 Service Locator 模式開發時,緩存引用是一個很好的主 意,或者說至少在當時沒有任何的傷害。但是自從 J2EE 1.3 開始,應用程序資源名是組 件范圍的,不是全局的了。因為每個組件單獨映射,兩個偶爾使用相同資源名的組件可能 不需要綁定到相同的容器資源。所以就需要重載,這樣使用相同名的組件就映射到不同的 資源。

緩存引用的服務定位器將導致 J2EE 1.3(以及以後版本)中有重載資源名的應用程序 工作不正常。這些程序可能部署成功並且表面上看起來運行正確,但當組件使用了錯誤的 資源時將引起微妙的並且難以診斷的問題。這是因為服務定位器將緩存不論哪個組件首次 使用時所重載的名稱的資源。當不同的綁定組件使用相同的資源名的時候,這個組件將不 能得到它所綁定的那個資源,它將得到在緩存中的資源,而這個資源就是第一個組件的資 源。

服務定位器何時會出錯

讓我們一步步的通過這個過程來看看具體是在哪裡出錯:

在部署階段,部署者綁定兩個資源,如上文所述( 表 4)。

讓我們從應用程序啟動後說起,第一個用戶是雇員並使用 EmployeeBean 訪問數據庫 。 EmployeeBean 使用緩存服務定位器訪問 jdbc/AccountDB

服務定位器沒有在它的緩存中找到 jdbc/AccountDB,因為此時緩存是空的。

reference cache { }

服務定位器執行 JNDI 查找並取得容器資源 jdbc/AccountEmployeeDS。服務定位器將 這個資源添加到緩存中 jdbc/AccountDB 關鍵字下面:

reference cache {jdbc/AccountDB jdbc/AccountEmployeeDS}

服務定位器返回容器資源 jdbc/AccountEmployeeDS。

現在正如所預料的那樣,EmployeeBean 中有了資源 jdbc/AccountEmployeeDS。

現在另外一個用戶,一個顧客,開始使用應用程序,使用 CustomerBean 訪問數據庫 。 CustomerBean 使用緩存服務定位器訪問 jdbc/AccountDB:

此時,服務定位器在它的緩存中找到了 jdbc/AccountDB。

reference cache {jdbc/AccountDB jdbc/AccountEmployeeDS}

服務定位器略過 JNDI 查找並取而代之的使用緩存中的資源。

服務定位器返回容器資源 jdbc/AccountEmployeeDS。

CustomerBean 現在擁有了資源: jdbc/AccountEmployeeDS。問題出現了: CustomerBean 現在擁有了 jdbc/AccountEmployeeDS,而不是它該擁有的 jdbc/AccountCustomerDS。 CustomerBean 現在使用錯誤的數據源。

檢測緩存問題

當兩個組件使用一個重載的資源名,也就是說,一個名稱綁定到兩個不同的資源,緩 存服務定位器為兩個組件返回相同的資源。這是一個非常微妙的錯誤,並且難以檢測和診 斷。

設想一下在我們的例子中錯誤的結果以及這是如何的難以診斷:

如果雇員先訪問,客戶取得了數據源,通過這個數據源他就可以訪問機密數據表。這 在功能測試中很難發現,特別地是在如果 Java 應用程序適當的組織了客戶對某些專有數 據的訪問時。如果可以檢測到這個問題,那麼只是會偶爾在雇員先到的時候發生這樣的錯 誤。只有偶爾會發生的問題是很難確定問題的起因的。

如果客戶先訪問,那麼就會使用客戶數據源,雇員就不能訪問機密數據表了。這個問 題更加明顯一些並且比較容易檢測,但卻很難解釋。靜態的代碼分析清楚的顯示了 EmployeeBean 使用雇員登錄數據源。而且,只是偶爾發生問題。要花費多長時間解決當 客戶先訪問時發生的問題呢?要解決由服務定位器導致的問題的難度有多大呢?

運行時證據

如果您正特別的尋找這個錯誤,那當然容易找到,就像您為正確的症狀在正確的位置 查找一樣。我實現了兩個無狀態會話 bean 類,他們都通過相同的資源名訪問數據源: jdbc/datasource。在 bean 訪問過它的數據源後,打印出對象的 toString()。這段代碼 在兩個 bean 裡是相同的:

public void persist()
    throws Exception {
  Context root = new InitialContext();
  DataSource ds = (DataSource) root.lookup ("java:comp/env/jdbc/datasource");
  System.out.println("The datasource is: " + ds.toString());
  // use the datasource to persist some data
}

servlet 創建每個 bean 的一個實例並且運行它。我也配置容器兩個不同的數據源。 當我部署我的應用程序時,我為每個數據源綁定一個 bean。

我的第一個實現沒有使用服務定位器, bean 本身執行所有的 JNDI 代碼。下面是我 所得到的兩個 toString() 的內容:

The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@2ecdaf6c
The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@75282f6b

那麼,上面的內容代表什麼呢?類 com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource 是專有的 WebSphere Application Server 類,實現 JDBC 數據源,這個類並不重要。重 要的部分是在末尾的十六進制的數字,他們是實例對象標識符(OID)。

注意到兩個 OID 不相同。這就顯示兩個 bean 使用兩個不同的數據源。

我第二個實現使用了緩存服務定位器。JNDI 查找代碼在服務定位器實現體內,實現緩 存資源的功能。由於兩個 bean 使用相同資源名: jdbc/datasource,所以服務定位器將 緩存第一個查找並將其重用於第二個 bean。下面是輸出結果:

The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@4652e820
The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@4652e820

注意到兩個 OID 是相同的。由於緩存服務定位器的原因,兩個 bean 現在使用相同數 據源。第二個 bean 使用第一個 bean 的數據源, 而這恰恰是錯誤的。

我的第三個實現使用沒有緩存的服務定位器。服務定位器代碼兩次查找資源,因為第 一次查找沒有被緩存起來。下面是輸出結果:

The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@4652e820
The datasource is:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@277d6821

這裡的 OID 是不同的。因為沒有緩存,每個 bean 都得到了正確的數據源。正如所預 測的那樣,重載的資源名和緩存服務定位器使得代碼執行有不同的結果,它使某些組件取 得錯誤的資源。

修改實現

我們已經看到了重載資源名和典型服務定位器實現,有資源緩存的集合,但他們之間 並沒有很好的合作。為何這樣呢?更重要的是,我們可以修改它麼?

為何緩存?

好,既然是由於緩存服務定位器導致重載資源名時的錯誤。我們為何又非得要緩存呢 ?

當首次使用 Service Locator 模式開發 J2EE 1.2 應用程序時,因為 JNDI 查找時常 降低甚至有損於執行性能,所以緩存資源引用在當時是一個很好的主意。J2EE 容器提供 商發現了執行性能方面的問題並在 J2EE 1.3 中改進了他們的實現。因此,對於 J2EE 1.3 來說,JNDI 查找執行的非常不錯,而此時緩存查找就沒有多大用處了。

不要只假設緩存服務定位器能顯著的提高應用程序的性能,要使用性能測試來證明這 一點。即使緩存能提高性能,當組件接收它所映射的資源失敗時,也是一件痛苦的事情。 “當然,組建取得了錯誤的資源當然不能正常工作,但是,要感謝緩存,它失敗的 卻是很快。”,執行迅速但不能正常工作的代碼並不是好的。

緩存甚至不是 Service Locator 模式的主要點。Service Locator 的推動是創建簡單 、整潔的 API,以一種統一的方式來定位、訪問其他服務和組建。緩存只是一種最優化。 它是想要使定位器將相同的功能執行的更快。並不是要改變定位器的行為,結果 必須是 相同的或沒有緩存。無疑地,正如本文所展示的,緩存可以改變定位器的返回結果,因此 就改變了應用程序的行為。

所以緩存並不是我們想要的。在正確的行為與快速的行為之間的抉擇,正確性每次都 會取得勝利。

為對象緩存的最好的方式是存儲任何的它要在實例變量中使用的 JNDI 引用。每個實 例必須初始化其自身的緩存,但它只緩存那些它要使用的引用,並且可以直接訪問引用而 不是要到一個散列映射中去尋找。試圖努力避免取得重復的相同值總是一個很好的實踐, 即使對可以緩存的服務定位器來說也是這樣。相反,只取得一次值,然後將其存儲在一個 變量中,以一個具有描述性的名稱表示它。然後就可以在需要的時候訪問這個變量。

為何重載資源名?

重載資源名使得服務定位器與其緩存之間變得混亂。除了去除緩存之外還有其他的選 擇麼?為和不避免資源名的重載?

將每個組件的資源名綁定到容器的資源名是 J2EE 1.3 規范的一部分。一個不支持這 點的應用程序就不是 J2EE 所允許的。應用程序在部署階段必須支持資源名綁定。這意味 著如果多個組件使用相同資源名的話,這個名稱在部署時期就會被重載。

可選的解決方法

既然一個共享的資源名可以變成重載的,那麼當我們使用緩存服務定位器時我們的組 件仍然會接收到正確的資源,這又是為何呢?下面就是一些操作:

每個組件一個唯一的資源名

一個方法是確保每個組件使用唯一的資源名。通過那個方法,沒有兩個組件使用相同 的資源名,因此就沒有名稱被重載。例如,EJB com.acme.AcmeEJB 使用容器資源 jdbc/datasource 可以實際的使用資源名稱 jdbc/com/acme/AcmeEJB/datasource。由於 每個 EJB 類都有一個唯一的包和類名,這種命名方法保證了每個組件的資源名將是唯一 的。雖然這是一種很笨重的實現方法。當不同組件類型計劃使用相同的資源時,這可以使 得它們輕易的使用相同的資源名。

每個組件類型一個唯一的服務定位器實例

另外一個方法是每個組件類型使用它自身的服務定位器實例,而不是集合。通過這個 方法,每個特定類型的組件將得到其自身的緩存引用,而不會與任何其它組件緩存有沖突 。然而,這實現起來很難。每個 Web 應用程序在何處存儲它的要與其他 Web 應用程序和 EJB 類型有區別的服務定位器呢?對 EJB 類型來說,每個類可以為它的服務定位器聲明 靜態變量,但 EJB 類的靜態變量必須是只讀的(因此就是 final 類型),這就要求重新 設計服務定位器類,以使他的無參數構造器不拋出異常。而且,良好分解的 EJB 通常代 表許多簡單的 Java 對象,這些對象將不能知道要使用哪個服務定位器,然後可能要以某 種方式調用 EJB 來取得。服務定位器作為集合的形式實現起來要簡單的多。

不要使用服務定位器

組件可以通過根本不使用服務定位器而避免重載緩存的名稱的問題,但是這種方法又 有點錯殺一千的感覺。 Service Locator 模式對封裝使用 JNDI 的代碼仍然有用,即使 定位器不使用緩存。如果把這個模式都全盤否定而失去了這個封裝性,那將是一件很不幸 的事。

剩下的一個選擇是使用服務定位器使用集合的方式,但是要去除引用緩存。將其應用 於現有應用程序很簡單:只要改變服務定位器實現去禁止或去除緩存。

在容器中緩存

最終,最好的回答是不在應用程序中緩存,而是緩存於容器當中。首先,這將對所有 可以緩存的應用程序有用,不論它們是否使用 Service Locator 模式。其次,容器可以 通過它們的容器名緩存資源,這些名稱必須是唯一的因此它們就不會在任何單獨的 J2EE 應用程序部署中被重載。

這只是恰巧發生在 WebSphere Application Server 緩存 JNDI 引用結果時。更多的 細節請參見 WebSphere Application Server Information Center(參加 參考資料)。

正如前文所述,每個對象也可以緩存它在實例變量中使用的 JNDI 引用,所以每個對 象在某一時刻只能訪問一個資源。

結束語

重載資源名實際開始於 J2EE 1.3。組件通常共享資源名,部署者可以映射組件並使其 對不同容器資源共享其名稱。帶有資源緩存的集合服務定位器創建全局緩存,通過這種方 式不能為重載的資源名正確處理組件級映射。

簡單的結論是:使用服務定位器進行緩存不是一個好的主意。包含緩存服務定位器的 應用程序遲早要出問題,而且這個問題將很難檢測、很難再現且很難診斷。毫無疑問會發 生問題,而且我可以保證:只要在部署階段為不同的資源綁定相同的資源名,這個問題就 會發生。

希望為組件使用相同的資源,共享相同的資源名仍是一個好主意。同樣,使用服務定 位器封裝資源訪問、以集合的形式使用服務定位器也是一個不錯的想法。但是服務定位器 不應包含資源緩存。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved