今天主要分析两个类的实现。
首先看一下 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 上的文章贴过来,便于一处维护,也便于与大家分享和讨论问题:-)
相关推荐
springboot+activemq, 用代码讲解了activemq的2中处理模式:PTP与PUB/SUB, 具体讲解已经在文章中介绍过了
源码为web工程,整合了Spring,SpringMVC、ActiveMQ,启动后,index页面输入消息,控制台输出消息。
使用doxygen生成的ActiveMQ 5.7源码API 直接根据代码注释生成的源码API 直接解压找到文件夹下index.html查看
activemq5.9源码
ActiveMQ 5 java 源码 直接从svn checkout 下来的代码。
Spring与ActiveMQ完整实例源码下载 Maven 版。 Spring与ActiveMQ完整实例整合源码,maven版本可以直接运行 百度云下载 SpringActiveMQ源码百度云下载
apache-activemq 5.7 源码及jar包 含帮助文档,源代码,例子等。项目可部署运行,亲测。
很全面的 activeMQ 源码分析,自己自己读过,与大家分享
ActiveMQ 源码
一: ActiveMQ简介 包括:是什么、能干什么、特点;消息中间件的功能、特点、应用场景等 n 二: ActiveMQ安装和基本使用 包括:通过源码安装、基本的配置示例、启动、测试运行、关闭等 n 三:理解和掌握JMS 包括:...
activeMQ学习activeMQ学习activeMQ学习activeMQ学习
ActiveMQ学习 完整例子
ActiveMQ-5.9和jms-1.1源码 博文链接:https://1197581932.iteye.com/blog/2353229
activemq-parent-5.16.2-source-release.zip,activemq-5.16.2-源码包
activemq 5.5.0的源代码,很全,对于研究activemq的人,很有帮助
ActiveMQ In Action及其源码 本来想免费提供下载的,但是发现csdn不能选择0积分下载,坑的很
此实例基于Spring+JMS+ActiveMQ+Tomcat,注解的完整实例,包含jar包
ActiveMQ环境搭建及实例详解的源码 ActiveMQ环境搭建及实例详解的源码
activemq-all-5.8.0.jar 下载 activemq-all-5.8.0.jar 下载 activemq-all-5.8.0.jar 下载 activemq-all-5.8.0.jar 下载 activemq-all-5.8.0.jar 下载
activeMQ 详细教程与源码(包含消费者与生产者),下载可根据文档修改配置文件后进行测试