今天主要分析两个类的实现。
首先看一下 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 上的文章贴过来,便于一处维护,也便于与大家分享和讨论问题:-)
相关推荐
消息队列:ActiveMQ:ActiveMQ的高级特性:虚拟目的地与代理.docx
总结,`spring整合Activemq源码`项目展示了如何在Spring和SpringMVC环境中利用ActiveMQ实现消息的发布与订阅。通过理解这些知识点,开发者能够更好地理解和实践消息队列在实际项目中的应用,提升系统的稳定性和扩展...
5. **集群与高可用性**:ActiveMQ支持集群模式,通过源码可以学习到如何配置和管理集群,实现消息的负载均衡和故障转移。 6. **安全与认证**:ActiveMQ支持JAAS(Java Authentication and Authorization Service)...
ActiveMQ中消息队列的实现是基于内存和磁盘的混合模式,既能提供高吞吐量又能保证消息的持久性。 - **Queue的结构**:ActiveMQ中的Queue是由多个Message Store组成,这些Message Store可以是内存中的也可以是磁盘上...
9. **插件体系**:ActiveMQ允许开发者通过插件自定义行为,源码中可以学习到如何编写和集成这些插件,以扩展其功能。 10. **日志和监控**:ActiveMQ使用了Log4j进行日志记录,并提供了监控工具,源码中可以了解如何...
通过研究这个源码包,开发者可以理解ActiveMQ如何处理消息的发布/订阅、队列、事务、红elivery策略等,以及如何自定义和扩展其功能,对于深入学习消息中间件和分布式系统有极大的帮助。同时,这也有助于开发者解决...
《ActiveMQ 5.7源码API详解》 Apache ActiveMQ是开源的、高性能的、功能丰富的消息中间件,它遵循JMS(Java Message Service)规范,为分布式系统提供了可靠的消息传递服务。ActiveMQ 5.7版本是其重要的一个里程碑...
3. **ActiveMQ配置**:在Spring中配置ActiveMQ,我们需要在`applicationContext.xml`或对应的配置文件中添加ActiveMQ的连接工厂和目的地(Topic或Queue)。这通常包括URL、用户名、密码等信息,以连接到ActiveMQ...
在这种模式下,每个消息只有一个消费者,消息发送者将消息放入队列,消费者从队列中取出并消费消息。队列中的一条消息在被一个消费者处理后会被自动删除,因此,同一消息不会被多个消费者处理。在SpringBoot中配置和...
这个源码包为开发者提供了了解和学习ActiveMQ实现细节的机会。 源码分析可以从以下几个关键知识点开始: 1. **JMS接口**:ActiveMQ是JMS规范的实现者,提供了生产、消费、管理消息的API。理解JMS接口(如...
通过阅读源码,我们可以学习到如何设计和实现一个高性能的消息代理,包括消息的存储和检索、消费者的管理、以及并发和性能优化策略。这对于想要定制ActiveMQ或进行二次开发的工程师来说,是一份宝贵的资源。 在实际...
消息队列:ActiveMQ:ActiveMQ消息类型:点对点与发布订阅.docx
在ActiveMQ 5.8版本中,我们可以通过源码分析来深入了解其内部工作原理和设计模式。 1. **JMS接口与实现**:ActiveMQ作为JMS提供者,实现了JMS规范中的ConnectionFactory、Destination、MessageProducer、...
开发者可以从测试代码中学习如何编写测试用例和保证代码的健壮性。 "资源达人分享计划"的标签表明这是一个社区共享的资源,意味着你可以在社区中寻求帮助,或者分享你对ActiveMQ的理解和经验。研究和理解ActiveMQ的...
在本教程中,我们将深入理解 ActiveMQ 的核心概念、使用方式以及如何通过源码学习其工作原理。首先,让我们了解消息队列的基本概念:消息队列是一种异步通信机制,它允许应用程序之间通过发送和接收消息来交换数据,...
通过深入学习和实践`Instant Apache ActiveMQ Messaging Application Development How-to源码`,开发者可以掌握如何在自己的应用中有效地利用ActiveMQ,实现高效、可靠的异步通信和解耦。这将有助于构建可扩展的、...
- 使用设计模式:ActiveMQ大量运用了工厂模式、观察者模式、适配器模式等,这有助于理解如何在大型项目中有效地组织代码。 - 多线程和并发:在处理高并发消息时,ActiveMQ如何使用线程池和同步机制保证正确性。 - ...
4. **学习设计模式**:源码中可能包含了许多优秀的设计模式,如工厂模式、观察者模式等,有助于提升开发者的设计能力。 5. **理解JMS规范**:通过JMS-1.1源码,开发者能更深入地理解消息传递的规范,确保应用符合...
ActiveMQ是中国最流行的开源消息中间件之一,它基于...在提供的`TestForActiveMQ`源码中,可能包含了更复杂的示例,比如消息的持久化、事务处理或者网络集群配置,通过学习这些源码,能更深入地掌握ActiveMQ的用法。
《Spring与ActiveMQ整合实战详解》 在Java开发领域,Spring框架和ActiveMQ是两个非常重要的...这个实例源码是一个很好的学习起点,可以帮助开发者深入理解Spring JMS和ActiveMQ的使用,为实际项目开发打下坚实的基础。