今天主要分析两个类的实现。
首先看一下 org.apache.activemq.broker.BrokerFactory 类。从类的命名上看似乎使用了?GoF?设计模式中的抽象工厂模式。我们通过源码来分析一下是否真的应用了这种模式。
?
?
class="java" name="code">public final class BrokerFactory { // ... public static BrokerService createBroker(URI brokerURI, boolean startBroker) throws Exception { if (brokerURI.getScheme() == null) { throw new IllegalArgumentException("Invalid broker URI, no scheme specified: " + brokerURI); } BrokerFactoryHandler handler = createBrokerFactoryHandler(brokerURI.getScheme()); BrokerService broker = handler.createBroker(brokerURI); if (startBroker) { broker.start(); } return broker; } }
?
BrokerFactory 的主要功能是根据 URI(指向某个配置文件)创建 BrokerService 实例,URI 的语法决定了所创建的 broker service 的具体类型,如 createBroker 方法实现中所示。
?
来回顾一下抽象工厂模式的结构。
抽象工厂模式包含两种类的继承层次关系。抽象工厂(abstract factory)和具体工厂(concret factory)组成的工厂类继承关系,以及由抽象产品(abstract product)和具体产品(concret product)组成的产品类继承关系。
?
在 BrokerFactory 中,由于其主要功能是要创建 broker service 实例,因而套用在抽象工厂模式下,BrokerService 就充当了模式中的抽象产品类,而 BrokerService 的子类,如 XBeanBrokerService 等,则扮演了具体产品的角色。
?
有了产品类继承关系,接下来的问题是,BrokerFactory 是否是作为抽象工厂模式中的抽象工厂类存在的呢?
?
继续看 createBroker 方法的实现,会发现 BrokerFactory 将 broker service 的创建转移给了某个 BrokerFactoryHandler 的实例,该实例是通过 createBrokerFactoryHandler 方法得到的。
?
private static final FactoryFinder BROKER_FACTORY_HANDLER_FINDER = new FactoryFinder("META-INF/services/org/apache/activemq/broker/"); public static BrokerFactoryHandler createBrokerFactoryHandler(String type) throws IOException { try { return (BrokerFactoryHandler) BROKER_FACTORY_HANDLER_FINDER.newInstance(type); } catch (Throwable e) { throw IOExceptionSupport.create("Could not load " + type + " factory:" + e, e); } }
?
createBrokerFactoryHandler 方法根据参数的值创建不同种类的 BrokerFactoryHandler 实例。而 BrokerFactoryHandler 被定义为接口类,它只提供了一个接口方法 createBroker,其作用也是用于创建 broker service 实例,因而它才是真正的 broker service 的抽象工厂。继续分析发现,BrokerFactoryHandler 的实现类中包含 XBeanBrokerFactoryHandler,因而可以肯定 BrokerFactoryHandler 及其实现类构成了抽象工厂模式中的工厂类继承层次关系。
?
接下来,我们来看另一个更有意思的工具类?FactoryFinder?的实现。在这个类的实现中会看到另一种设计模式的应用,以及有关多线程的考虑。
?
?
public class FactoryFinder { /** * The strategy that the FactoryFinder uses to find load and instantiate Objects * ... */ public interface ObjectFactory { /** * @param path the full service path * @return */ public Object create(String path) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException; } /** * The default implementation of Object factory which works well in standalone applications. */ protected static class StandaloneObjectFactory implements ObjectFactory { // ... } // ================================================================ // Class methods and properties // ================================================================ private static ObjectFactory objectFactory = new StandaloneObjectFactory(); public static ObjectFactory getObjectFactory() { return objectFactory; } public static void setObjectFactory(ObjectFactory objectFactory) { FactoryFinder.objectFactory = objectFactory; } // ================================================================ // Instance methods and properties // ================================================================ private final String path; public FactoryFinder(String path) { this.path = path; } public Object newInstance(String key) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException { return objectFactory.create(path+key); } }
?
代码注释中已经给出了这种设计模式:策略模式?Strategy。FactoryFinder 类的 ObjectFactory 实例属性定义了用于加载和实例化对象的策略,并以内部类的形式定义了缺省的策略实现 — StandaloneObjectFactory。如果想修改策略,可以自定义实现 ObjectFactory 接口的类,并通过 setObjectFactory 方法将策略应用与 FactoryFinder。
?
我们来看 StandaloneObjectFactory 的实现,只看 create 方法:
?
protected static class StandaloneObjectFactory implements ObjectFactory { final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String, Class>(); @Override public Object create(final String path) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException { Class clazz = classMap.get(path); if (clazz == null) { clazz = loadClass(loadProperties(path)); classMap.put(path, clazz); } return clazz.newInstance(); } // loadClass(Properties properties) // Properties loadProperties(String uri) }
?
这里用到了 ConcurrentHashMap,它是 Java 5 提供的一个线程安全的 HashMap 实现。
?
ConcurrentHashMap 还能允许多个线程对 Map 的操作(读、写)以无阻塞的方式进行。在 Java 5 之前,还有一种线程安全的 HashMap 实现,Collections.synchronizedMap(Map),它的实现中用到了 bloking Map,意即当多个线程同时访问该 Map 时,只有一个线程处在活跃状态,其他线程会被阻塞,因而可能会影响性能。关于这两种实现的对比讨论,请参考?SO 上的这个问答。
?
我这里关心的是另一个细节。create 方法应该是希望实现相同的 path 返回同一个 Class 的。
?
Class clazz = classMap.get(path); if (clazz == null) { clazz = loadClass(loadProperties(path)); classMap.put(path, clazz); } return clazz.newInstance();
?
刚才说道,ConcurrentHashMap 是线程安全的,但是上面这一小段代码却有可能存在一致性的问题,即它并不符合线程安全的条件。
当然,考虑到 StandaloneObjectFactory 中实际的应用场景,同一个 path 一定会返回相同的 clazz,因而上面这段代码不会导致不一致。
?
如果不允许覆盖,更好的实现我认为是这样的:
?
Class clazz = classMap.get(path); if (clazz == null) { clazz = loadClass(loadProperties(path)); Class ret = classMap.putIfAbsent(path, clazz); if (ret != null) clazz = ret; } return clazz.newInstance();
?
用 ConcurrentHashMap 的 putIfAbsent 往 Map 中放 Key-Value,并根据返回值判断原来的 Map 中是否已经存在该 Key-Value 对。
?
完。
?
** 这篇文章属于原创,最早在7月28号发布在?Wordpress,转到这里算是对今天阅读的一篇文章(Why I Love Reading Other People’s Code And You Should Too?)的响应。很长一段时间以来,我都不满意 iteye 的排版,加之这篇文字最早是使用 markdown 语法写的,而我发现 wordpress 对 markdown 文件转成的 html 的支持很赞(几乎可以直接复制粘贴,效果见前面的 wordpress 链接),所以并没有发到这里。直到最近逐渐习惯了 iteye 的排版之后,决定把之前在 wordpress 上的文章贴过来,便于一处维护,也便于与大家分享和讨论问题:-)