程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> EJB的最佳實踐:工業強度的JNDI優化

EJB的最佳實踐:工業強度的JNDI優化

編輯:關於JAVA

在這篇技巧文章中,我們將研究一些最常用的 JNDI 優化。特別地,我們將向您展示如何將高速緩存和通用助手類組合使用,以創建針對 JNDI 開銷的工廠風格的解決方案。

減少上下文實例

清單 1 顯示了一段典型的 EJB 代碼,它需要多次 JNDI 查找。請花一點時間研究代碼,然後我們將對它進行優化以獲得更佳性能。

清單 1. 典型的 EJB 查找

public boolean buyItems(PaymentInfo paymentInfo, String storeName,
List items) {
// Load up the initial context
Context ctx = new InitialContext();
// Look up a bean´s home interface
Object obj = ctx.lookup("java:comp/env/ejb/PurchaseHome");
PurchaseHome purchaseHome =
(PurchaseHome)PortableRemoteObject.narrow(obj, PurchaseHome.class);
Purchase purchase = purchaseHome.create(paymentInfo);
// Work on the bean
for (Iterator i = items.iterator(); i.hasNext(); ) {
purchase.addItem((Item)i.next());
}
// Look up another bean
Object obj = ctx.lookup("java:comp/env/ejb/InventoryHome");
InventoryHome inventoryHome =
(InventoryHome)PortableRemoteObject.narrow(obj, InventoryHome.class);
Inventory inventory = inventoryHome.findByStoreName(storeName);
// Work on the bean
for (Iterator i = items.iterator(); i.hasNext(); )
inventory.markAsSold((Item)i.next());
}
// Do some other stuff
}

盡管這個示例多少有點刻意,但它確實揭示了使用 JNDI 時的一些最明顯的問題。對於初學者,您應該問問自己,新建 InitialContext 對象是否必需。很可能在應用程序代碼的其它地方已經裝入了這個上下文,而我們又在這裡創建了一個新的。高速緩存 InitialContext 實例會立即促使性能提高,如清單 2 所示:

清單 2. 高速緩存 InitialContext 實例

public static Context getInitialContext() {
if (initialContext == null) {
initialContext = new InitialContext();
}
return initialContext;
}

通過對 getInitialContext() 使用助手類,而不是為每個操作都實例化一個新的 InitialContext,我們將遍布在應用程序中的上下文數量減少為一個。

線程化會怎麼樣?

如果您對此處提出的解決方案的線程化感到擔心,那大可不必。兩個線程同時進行 getInitialContext() 是絕對有可能的(從而一次創建兩個上下文),但只有首次調用該方法時才會發生此類錯誤。因為問題至多只會發生一次,所以同步是不必要的,實際上,同步引入的復雜性比它所解決的復雜性更多。

優化查找

高速緩存上下文實例這個步驟的方向是正確的,但僅這樣做,還不足以完成優化。我們每次調用 lookup() 方法時都會執行一次新查找,並返回 bean 的 home 接口的新實例。至少,JNDI 查找通常是這樣編碼的。但如果每個 bean 都只有一個 home 接口,並在多個組件上共享這個接口,這樣不是更好嗎?

我們可以高速緩存每個單獨的 bean 引用,而不是反復查找 PurchaseHome 或 InventoryHome 的 home 接口;這是一種解決方案。但我們真正想要的是一種更通用的機制:在 EJB 應用程序中高速緩存 home 接口。

答案是創建通用助手類,它既可以為應用程序中的每個 bean 獲取初始上下文,又可以為它們查找 home 接口。此外,這個類還應該能夠為各種應用程序組件管理每個 bean 的上下文。清單 3 中所示的通用助手類將充當 EJB home 接口的工廠:

清單 3. EJB home 接口工廠

package com.ibm.ejb;
import java.util.Map;
import javax.ejb.EJBHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBHomeFactory {
private static EJBHomeFactory;
private Map homeInterfaces;
private Context context;
// This is private, and can´t be instantiated directly
private EJBHomeFactory() throws NamingException {
homeInterfaces = new HashMap();
// Get the context for caching purposes
context = new InitialContext();
/**
* In non-J2EE applications, you might need to load up
* a properties file and get this context manually. I´ve
* kept this simple for demonstration purposes.
*/
}
public static EJBHomeFactory getInstance() throws NamingException {
// Not completely thread-safe, but good enough
// (see note in article)
if (instance == null) {
instance = new EJBHomeFactory();
}
return instance;
}
public EJBHome lookup(String jndiName, Class homeInterfaceClass)
throws NamingException {
// See if we already have this interface cached
EJBHome homeInterface = (EJBHome)homeInterfaces.get(homeClass);
// If not, look up with the supplied JNDI name
if (homeInterface == null) {
Object obj = context.lookup(jndiName);
homeInterface =
(EJBHome)PortableRemoteObject.narrow(obj, homeInterfaceClass);
// If this is a new ref, save for caching purposes
homeInterfaces.put(homeInterfaceClass, homeInterface);
}
return homeInterface;
}
}

EJBHomeFactory 類內幕

home 接口工廠的關鍵在 homeInterfaces 映射中。該映射存儲了供使用的每個 bean 的 home 接口;這樣,home 接口實例可以反復使用。您還應注意,映射中的關鍵並不是傳遞到 lookup() 方法的 JNDI 名稱。將同一 home 接口綁定到不同 JNDI 名稱是很常見的,但這樣做會在您的映射中產生副本。通過依靠類本身,您就可以確保最終不會為同一個 bean 創建多個 home 接口。

將新的 home 接口工廠類插入清單 1 的原始代碼,這樣將會產生優化的 EJB 查找,如清單 4 所示:

清單 4. 改進的 EJB 查找

public boolean buyItems(PaymentInfo paymentInfo, String storeName,
List items) {
EJBHomeFactory f = EJBHomeFactory.getInstance();
PurchaseHome purchaseHome =
(PurchaseHome)f.lookup("java:comp/env/ejb/PurchaseHome",
PurchaseHome.class);
Purchase purchase = purchaseHome.create(paymentInfo);
// Work on the bean
for (Iterator i = items.iterator(); i.hasNext(); ) {
purchase.addItem((Item)i.next());
}
InventoryHome inventoryHome =
(InventoryHome)f.lookup("java:comp/env/ejb/InventoryHome",
InventoryHome.class);
Inventory inventory = inventoryHome.findByStoreName(storeName);
// Work on the bean
for (Iterator i = items.iterator(); i.hasNext(); ) {
inventory.markAsSold((Item)i.next());
}
// Do some other stuff
}

隨著時間的推進,除了更清晰之外(至少按我的觀點),以上工廠優化的 EJB 查找將執行得更快。您第一次使用這個新類時,將花費所有正常查找開銷(假定應用程序的其它部分沒有付出過這種開銷),但將來的所有 JNDI 查找都將繼續使用原先的查找結果。還有必要指出,home 接口工廠不會干擾您容器的bean 管理。容器管理的是 bean 實例,而不是這些 bean 實例的 home 接口。您的容器還將管理實例交換,以及其它您希望它執行的任何優化。

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