Service Locator 是 J2EE 應用中常見的設計模式,透過封裝 JNDI 目錄服務查找程式碼,讓用戶端以資源名稱取得資源。傳統實作常包含資源快取以避免重複查找,這在 J2EE 1.2 運作良好,但在 J2EE 1.3 及後續版本中,快取會引入微妙且難以診斷的錯誤。因此,J2EE 1.3 應用的 Service Locator 實作不應包含資源快取。
Java Naming and Directory Interface (JNDI) 讓 Java 程式透過唯一名稱存取資源,屏蔽儲存位置、實作細節與提供者差異。JNDI 名稱採層次樹狀結構,J2EE 規範定義了標準子上下文(如 java:comp/env/jdbc 對應 JDBC 資料源)。應用程式伺服器建構自身 JNDI 樹,整合 JDBC、JMS 等資源,使元件能以統一方式存取本地或遠端資源,支援元件重用。

J2EE 採雙重命名機制:
jdbc/AccountDB)。jdbc/Cloudscape)。部署時由管理員將參考名稱映射至 JNDI 名稱。J2EE 1.2 中資源名稱為全域,J2EE 1.3 引入元件級作用域:每個 EJB 或 Web 模組擁有獨立的資源名稱空間。這導致資源名稱重載——相同參考名稱在不同元件可映射至不同容器資源。例如 EmployeeBean 與 CustomerBean 均使用 jdbc/AccountDB,但部署時分別綁定至 jdbc/AccountEmployeeDS(完整權限)與 jdbc/AccountCustomerDS(唯讀權限),以滿足安全需求。
典型 Service Locator 以單例實作,內含 Map 快取資源查找結果。首次查找後存入快取,後續請求直接返回快取物件。此機制在 J2EE 1.2 全域名稱下無害,但在 J2EE 1.3 重載名稱下將導致嚴重錯誤:
EmployeeBean,查找 jdbc/AccountDB 獲取 AccountEmployeeDS 並存入快取。CustomerBean 請求同名稱,Service Locator 直接返回快取的 AccountEmployeeDS,導致客戶端取得員工權限資料源,違反安全策略。此錯誤極難偵測:若員工先登入,客戶端竊取機密權限;若客戶先登入,員工無法存取機密表單。錯誤僅在特定順序下出現,靜態程式碼分析無法發現。
作者透過兩個無狀態 Session Bean 驗證:
為何曾需要快取? J2EE 1.2 時代 JNDI 查找效能較差,快取可提升效能。但 J2EE 1.3 容器已優化 JNDI 查找效能,快收益微乎其微。且快取改變了定位器行為,違反「正確性優於速度」原則。
替代方案分析:
J2EE 1.3 引入的資源名稱重載機制,使帶快取的單例 Service Locator 成為錯誤溫床。結論明確:Service Locator 不應包含資源快取。保留模式封裝 JNDI 查找、以單例提供統一存取介面,但移除快取邏輯,確保正確性與可攜性。