`
wxl24life
  • 浏览: 290737 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ActiveMQ 源码学习 1:从源码中找寻设计模式的踪影

阅读更多

今天主要分析两个类的实现。

首先看一下 org.apache.activemq.broker.BrokerFactory 类。从类的命名上看似乎使用了 GoF 设计模式中的抽象工厂模式。我们通过源码来分析一下是否真的应用了这种模式。

 

 

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 是线程安全的,但是上面这一小段代码却有可能存在一致性的问题,即它并不符合线程安全的条件。

  • 假设 A 和 B 两个线程同时执行,在某个时刻 A 和 B 都从 classMap 中取到同一个 path 对应的 clazz,且均检查发现 clazz 是 null。之后 A 和 B 分别根据 path 计算自己的 clazz,并先后执行 put 语句。如果 A 和 B 计算得到的 clazz 不同,先后执行 put 就会出现后面覆盖前面的情况。因而,最终 classMap 中存放的值将取决与时序关系。

当然,考虑到 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 上的文章贴过来,便于一处维护,也便于与大家分享和讨论问题:-)
2
3
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics