利用ServiceLocator模式改进JNDI实践
J2EE應(yīng)用系統(tǒng)是打了“分布式”的標(biāo)簽的,所以客戶端需要定位業(yè)務(wù)層的組件和服務(wù),常見的比如有:EJB Home接口、EJB LocalHome接口、JMS消息列隊或主題、JMS消息列隊工廠等等,當(dāng)然還有再普通不過的JAVA對象了,那么對這些分布在不同位置的組件和服務(wù),客戶端是如何進行尋址的呢?這就是JNDI的任務(wù)了...
JNDI(The Java Naming and Directory Interface,Java命名和目錄接口)是一組在Java應(yīng)用中訪問命名和目錄服務(wù)的API。如同其它很多Java技術(shù)一樣(例如 JDBC),JDNI是provider-based的技術(shù),暴露了一個API和一個服務(wù)供應(yīng)接口(SPI)。這意味著任何基于名字的技術(shù)都能通過 JNDI而提供服務(wù),只要JNDI支持這項技術(shù)。JNDI目前所支持的技術(shù)包括LDAP、CORBA Common Object Service(COS)名字服務(wù)、RMI、NDS、DNS、Windows注冊表等等。JDNI通過綁定的概念將對象和名稱聯(lián)系起來。就像在一個文件系統(tǒng)中,文件名被綁定給文件;在DNS中,一個IP地址綁定一個URL;在目錄服務(wù)中,一個對象名被綁定給一個對象實體。在J2EE應(yīng)用中,業(yè)務(wù)層組件(比如EJB組件)和集成層組件(比如JDBC數(shù)據(jù)源和JMS組件)通常都要在一個中央注冊表中注冊。客戶端使用JNDI API來與這個注冊表交互,獲得一個初始化上下文(InitialContext)對象,該對象中裝載著組件的名稱/對象綁定信息。客戶端通過它就可以 lookup我們需要的組件和服務(wù)了,但是這樣可能會相當(dāng)?shù)膹?fù)雜,因為這可能要包括重復(fù)使用初始化上下文(InitialContext)對象、尋址操作、強制類型轉(zhuǎn)換操作以及對底層異常和超時的處理操作。
想一想,我們是不是在這樣不停地重復(fù)我們自己呢?當(dāng)我們粘貼復(fù)制到手都發(fā)麻的時候,有沒有考慮如果現(xiàn)在要對其進行一點改動,我們又該怎么作?我猜想你一定會責(zé)怪自己為什么才發(fā)現(xiàn)呢, 還要在從頭粘貼復(fù)制,噢,天呢!應(yīng)用系統(tǒng)客戶端需要從這種復(fù)雜性中隔離出來。不然,在多種不同的客戶端中就會有重復(fù)的JNDI代碼,因為所有訪問一個由JNDI管理的服務(wù)/組件的客戶端都要執(zhí)行同樣的尋址。創(chuàng)建一個JNDI InitialContext對象,執(zhí)行服務(wù)組件的尋址可能是一種代價很高的操作,如果反復(fù)執(zhí)行這樣的操作,可能會引起系統(tǒng)性能的惡化。另外一個問題是,JDNI是provider-based的技術(shù),InitialContext和JNDI注冊表中注冊的其他context工廠都是廠商提供實現(xiàn)的。如果應(yīng)用系統(tǒng)客戶端直接訪問以上的對象的這些特殊實現(xiàn),那么會給應(yīng)用引入廠商依賴性,使代碼難以移植。
所以我們需要一種統(tǒng)一的、透明的方式定位業(yè)務(wù)組件和業(yè)務(wù)服務(wù)。J2EE核心模式里面的ServiceLocator模式就是這個問題的最優(yōu)解,使用服務(wù)定位器實現(xiàn)、封裝對服務(wù)/組件的尋址。應(yīng)用系統(tǒng)客戶端可以通過服務(wù)定位器實現(xiàn)重用,降低代碼的復(fù)雜性,提供唯一的控制點,并且提供緩存機制(Cache),能夠改善系統(tǒng)的性能。下面通過一個定位EJB服務(wù)的例子來看一下怎么來實現(xiàn)ServiceLocator,對于其他服務(wù)和組件的定位就是大同小異的了。
import java.util.*; import javax.naming.*; import java.rmi.RemoteException; import javax.ejb.*; import javax.rmi.PortableRemoteObject; import java.io.*; public class ServiceLocator { private static ServiceLocator _instance; private InitialContext context = null; private Map cache; static { try{ _instance=new ServiceLocator(); } catch(ServiceLocatorException se) { System.err.println(se); se.printStackTrace(System.err); } } private ServiceLocator() throws ServiceLocatorException { try { context = new InitialContext(); cache=Collection.synchronizedMap(new HashMap()); } catch(NamingException ne) { throw new ServiceLocatorException(ne); } catch(Exception e) { throw new ServiceLocatorException(ne); } } public static ServiceLocator getInstance() throws ServiceLocatorException { return _instance; } public EJBLocalHome getLocalHome(String jndiHomeName) throws ServiceLocatorException { EJBLocalHome localHome = null; try { if(cache.containsKey(jndiHomeName)) { localHome = (EJBLocalHome)cache.get(jndiHomeName); } else { localHome = (EJBLocalHome)context,lookup(jndiName); cache.put(jndiName, localHome); } } catch(NamingException nex) { throw new ServiceLocatorException(ne); } catch(Exception ex) { throw new ServiceLocatorException(ne); } return localHome; } public EJBHome getRemoteHome(String jndiHomeName, Class homeClassName) throws ServiceLocatorException { EJBHome remoteHome = null; try { if(cache.containsKey(jndiHomeName)) { remoteHome = (EJBHome)cache.get(jndiHomeName); } else { Object objref = context,lookup(jndiName); Object obj = PortableRemoteObject. narrow(objref, homeClassName); remoteHome = (EJBHome)obj; cache.put(jndiName, remoteHome); } } catch(NamingException nex) { throw new ServiceLocatorException(ne); } catch(Exception ex) { throw new ServiceLocatorException(ne); } return remoteHome; } } 我們看到了ServiceLocator的核心采用了單子模式,利用ServiceLocator模式對JDNI的使用的進行改進,可以抽象系統(tǒng)的復(fù)雜性,向客戶端隱藏了JDNI尋址的復(fù)雜性;可以為客戶端提供統(tǒng)一的服務(wù)訪問方式,這種統(tǒng)一減少了開發(fā)和維護的工作量,我們終于可以免除粘貼復(fù)制了;提高了系統(tǒng)的網(wǎng)絡(luò)性能和客戶端性能。好了,現(xiàn)在就開始重構(gòu)我們的JNDI代碼吧!
總結(jié)
以上是生活随笔為你收集整理的利用ServiceLocator模式改进JNDI实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: itextPdf~将PDF页面大小转为A
- 下一篇: Guava入门~Objects