欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-20 17:51:39
...

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

首先看一下 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 上的文章贴过来,便于一处维护,也便于与大家分享和讨论问题:-)

上一篇: maven使用插件

下一篇: CloudStack简史