使用依赖注入能够消除MovieLister对具体实现MovieFinder的依赖。这允许我把Movielister
但注入并不是解除依赖关系的唯一方法,还有一种方法也可以解除依赖关系,那就是使用服务定位器模式。
模式内容
服务定位器的最基本的思想就是有一个对象定位器知晓如何控制应用程序需要的所有服务。所以根据Lister&Finder例子提到的应用程序的一个服务定位器在需要的时候将会使用某个方法返回一个movie finder的实例。当然这仅仅是转移一些负担而已,我们仍旧必须取得进入lister的服务定位器,结果就呈现以下的依赖关系。
模式示例
MovieFinder finder = ServiceLocator.movieFinder(); class ServiceLocator... public static MovieFinder movieFinder() { return soleInstance.movieFinder; } private static ServiceLocator soleInstance; private MovieFinder movieFinder;
就像注入方法一样,我们必须配置服务定位器(class ServiceLocator...)。下面我用代码来完成这个功能,但是使用从配置文件读取相应数据的机制也并不困难。
class Tester... private void configure() { ServiceLocator.load(new ServiceLocator(new ColonMovieFinder("movies1.txt"))); } class ServiceLocator... public static void load(ServiceLocator arg) { soleInstance = arg; } public ServiceLocator(MovieFinder movieFinder) { this.movieFinder = movieFinder; } class Tester... public void testSimple() { configure(); MovieLister lister = new MovieLister(); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the Wes", movies[0].getTitle()); }
在对ServiceLocator的具体实现上还有很多技巧可以使用,
1.在初始化方式上,可以采用子类继承父类的方式,在父类初始化阶段 就得到 静态对象
2.还可以通过接口的方式返回一个movieFinder 这样 具体实现跟实际调用就通过接口隔离开了
3.还可以通过线程相关的存储机制来提供线程相关的定位器
4.还可以将几种movieFinder的实现,放入到Map中,动态的生成实例,以构建动态定位器
模式实现
import javax.naming.*; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; import javax.ejb.EJBHome; import javax.ejb.EJBLocalHome; import javax.sql.DataSource; import java.util.*; import java.sql.*; /** *//** * 实现 service locater 模式,用于由客户端来调用以通过JNDI查 * 找相关的 ejb或是其它服务的入口. * */ public final class ServiceLocater { protected static ServiceLocater inst = new ServiceLocater(); private InitialContext ic = null; private Map ejbHomeCache = null; private Map dataSourceCache = null; protected ServiceLocater() { try { dataSourceCache = Collections.synchronizedMap(new HashMap()); ejbHomeCache = Collections.synchronizedMap(new HashMap()); ic = new InitialContext(); } catch (Exception e) { e.printStackTrace(); } } /** *//** * 取得 servicelocater的单子实例. * */ synchronized public static ServiceLocater getInstance() { return inst; } /** *//** *查找并返回一个数据源 * @param name String 数据源名称 * @return DataSource ,查找不到则抛出异常. * @throws NamingException ,查找不到或是类型不对。 * */ private DataSource lookUpDataSource(String name) throws NamingException { DataSource tmpDS = (DataSource)this.dataSourceCache.get(name); if (tmpDS == null) { try { tmpDS = (DataSource)this.ic.lookup(name); this.dataSourceCache.put(name, tmpDS); } catch (NamingException namE) { throw namE; } catch (Exception otherE) { throw new NamingException(otherE.getMessage()); } } return tmpDS; } /** *//** * 查找并返回一个远程接口 * @param jndiHomeName ebj名字 * @param className ejb类名字 * @return * @throws ServiceLocatorException */ public EJBHome getRemoteHome(String jndiHomeName, Class className) throws ServiceLocatorException { EJBHome home = (EJBHome)this.ejbHomeCache.get(jndiHomeName); if (home == null) { try { Object objref = ic.lookup(jndiHomeName); Object obj = PortableRemoteObject.narrow(objref, className); home = (EJBHome) obj; this.ejbHomeCache.put(jndiHomeName, home); } catch (NamingException ne) { throw new ServiceLocatorException(ne); } catch (Exception e) { throw new ServiceLocatorException(e); } } return home; } /** *//** * 查找并返回一个本地接口 * @param jndiHomeName jndiHomeName名字 * @return 一个本地接口 * @throws ServiceLocatorException */ public EJBLocalHome getLocalHome(String jndiHomeName) throws ServiceLocatorException { EJBLocalHome home = null; try { home = (EJBLocalHome) ic.lookup(jndiHomeName); } catch (NamingException ne) { throw new ServiceLocatorException(ne); } catch (Exception e) { throw new ServiceLocatorException(e); } return home; } /** *//** *查找一个数据源,并取得一个连接. * @param name String 数据源名称 * @return DataSource ,查找不到则抛出异常. * @throws NamingException ,查找不到或是类型不对。 * */ public Connection getConnection(String DataSourceJNDIName) throws SQLException { try { Connection conn = this.lookUpDataSource(DataSourceJNDIName).getConnection(); conn.setAutoCommit(false); //conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); return conn; } catch (Exception e) { e.printStackTrace(); throw new SQLException(e.getMessage()); } } }