EJB 客戶端應用形式多樣,可以是 Applet、獨立應用程式、Servlet 或另一個 Bean。其核心優勢在於:編程模型簡單、無過度開銷,且能透明地存取分散式企業級服務。
JNDI (Java Naming and Directory Interface) 提供統一的命名服務,類似電話黃頁,將邏輯名稱綁定到實際物件。核心組件包括:
InitialContextbind(name, obj)、lookup(name)解決「客戶端不知道如何定位 EJB 伺服器」的問題。透過命名服務器,多台伺服器可構成統一命名空間,客戶端只需知道:
public Context getInitialContext() throws NamingException {
Hashtable props = new Hashtable(2);
props.put(Context.PROVIDER_URL, getNameServerURL());
props.put(Context.INITIAL_CONTEXT_FACTORY, getInitialContextFactoryClassName());
return new InitialContext(props);
}
最佳實踐:廠商特定常數 (如 INITIAL_CONTEXT_FACTORY, PROVIDER_URL) 不應硬編碼,應從屬性檔或 Servlet init 參數讀取。
public MyBeanHome findMyBeanHome() throws NamingException {
Context context = getInitialContext();
try {
Object object = context.lookup("MyBean");
return (MyBeanHome) PortableRemoteObject.narrow(object, MyBeanHome.class);
} finally {
context.close(); // 及時關閉,Context 非執行緒安全
}
}
PortableRemoteObject.narrow() 是 RMI-IIOP 環境下的類型安全轉換,替代傳統強制轉型。
try {
MyBean bean = findMyBeanHome().create(); // 或 findByPrimaryKey(...)
bean.computeSomethingOrOther(42);
// 無需顯式清理,僅停止引用即可
} catch (NamingException e) {
// 處理命名異常
}
JNDI 查找開銷較大,建議快取 Home 介面引用:
public synchronized BankAccountHome getBankAccountHome() throws NamingException {
if (bankAccountHome == null)
bankAccountHome = findBankAccountHome();
return bankAccountHome;
}
注意:Home/Bean 引用不可序列化,不適用於跨 JVM 傳遞。
public void doGet(...) throws ... {
PrintWriter out = response.getWriter();
try {
BankAccount account = getBankAccountHome().findById(42);
double balance = account.getBalance();
out.println("Balance is: " + balance);
} catch (Exception e) {
out.println("A problem has occurred with the server.");
}
}
finally 區塊關閉掌握 JNDI 與 Home 介面的正確使用模式,是構建健壯 EJB 客戶端應用的基石。