Spring整合ActiveMQ的Demo

本文详细介绍ActiveMQ消息中间件的配置及使用方法,包括基础配置、多种消息监听方式、消息转换及持久化技术,并提供实例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目的代码:http://git.oschina.net/jsc/ActiveMQDemo,Maven项目。

我是以配置文件appicationContext.xml为目录从上往下,其中每组配置就是一组消息队列实例,每个实例功能点不同。发送消息方面比较单调,我都写在ProducerComponent类中,监听消息方面就每个实例写一个监听器,因为功能不同。

 

目录:

  • ActiveMQ服务的启动、基本的配置

  • 实例1:入门例子

  • 实例2:使用SessionAwareMessageListener

  • 实例3:使用MessageListenerAdapter

  • 实例4:MessageListenerAdapter绑定其他监听方法

  • 实例5:MessageListenerAdapter进行回复

  • 实例6: 消息转换器MessageConverter

  • 实例7:MessageListenerAdapter绑定消息转换器

  • 持久化到MySql

     

     

     

 

ActiveMQ服务的启动、基本的配置

百度上搜activemq从官网上下载5.13版(目前最新版),解压后在bin目录有win32和win64两个文件夹,根据自己系统位数双击对应文件夹中的activemq.bat启动,如果显示下面信息则表示成功。

100715_yFTC_2000700.png

我第一次启动不成功,报的错误忘记了,但是后来也没做什么又能启动了,以后都没问题。所以不能启动请根据错误在网上找找,正常是直接双击就启动的。

项目用的是spring整合的,整合最基本的配置是:

<!-- ActiveMQ提供的连接到ActiveMQ服务的类 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <!-- ActiveMQ默认服务端口为61616 -->
    <property name="brokerURL" value="tcp://localhost:61616" />
</bean>

<!-- Spring提供的jms连接消息队列的桥梁,有两个:SingleConnectionFactory和CachingConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>

<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
</bean>

这三个bean是最基本的配置,后面的配置基本都是一组消息队列实例。

 

实例1:入门例子

<!-- 实例1 -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg>
        <value>queueDestination</value>
    </constructor-arg>
</bean>

<!-- jms提供的消息监听器器 -->
<bean id="consumerMessageListener" class="demo.jms.listener.ConsumerMessageListener" />

<bean id="consumerMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="queueDestination" />
    <property name="messageListener" ref="consumerMessageListener" />
</bean>

每一个消息队列实例都需要这三个bean,第一个bean表示的是消息发送的目的地,构造器传的是队列名字,第二个bean表示谁去监听这个队列有消息了,第三个是容器,即将目的地、监听器、还有连接工厂关联起来,bean的id自己随便取。

发送消息很简单,注入jmsTemplate这个bean,调用它的send方法即可,参见ProducerComponent类。发送的消息有4种:TextMessage、BytesMessageMapMessage和ObjectMessage。

监听器:从配置中可以看到对应的监听器类是ConsumerMessageListener

调试方法:TestDemo类testSend方法。右键该类 --> Run As -->JUnit Test

发送接受都是异步的,测试结果输出:

[生产者发送了一条文本消息] testSend的消息(0)
[ConsumerMessageListener接收到一条消息] testSend的消息(0)
[生产者发送了一条文本消息] testSend的消息(1)
[ConsumerMessageListener接收到一条消息] testSend的消息(1)

 

有时候是:

[生产者发送了一条文本消息] testSend的消息(0)
[生产者发送了一条文本消息] testSend的消息(1)
[ConsumerMessageListener接收到一条消息] testSend的消息(0)
[ConsumerMessageListener接收到一条消息] testSend的消息(1)

 

实验总结:发送消息比较简单不说了,监听器需要实现MessageListener接口,实现onMessage方法。

 

 

实例2:使用SessionAwareMessageListener

配置请从applicableContext.xml中搜索“实例2”。

与实例1不同之处:监听器实现的不是MessageListener而是SessionAwareMessageListener,onMessage多了个Session参数,表示当前的连接,可以用来发送消息,如何发送参见ConsumerSessionAwareMessageListener类。

监听器:ConsumerSessionAwareMessageListener

调试方法:TestDemo类testSend2方法。

实验总结:用MessageListener想发送消息,步骤和ProducerComponent类一样,但是连接是新建的。而SessionAwareMessageListener的Session保存的则是监听器与服务之间当前的连接,不需要新建。

 

 

实例3:使用MessageListenerAdapter

配置请从applicableContext.xml中搜索“实例3”。

其中监听器是这样配置的:

<bean id="consumerMessageListenerAdapter"
		class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <!-- 默认调用方法handleMessage -->
    <constructor-arg>
        <bean class="demo.jms.listener.ConsumerMessageListenerAdapter" />
    </constructor-arg>
</bean>

监听器类不需要实现任何接口,以构造参数的形式注入给该bean,默认调用handleMessage方法。

 

MessageListenerAdapter会把接收到的消息做如下转换:

TextMessage转换为String对象;

BytesMessage转换为byte数组;

MapMessage转换为Map对象;

ObjectMessage转换为对应的Serializable对象。

所以监听的方法的参数类型用上面4种转换后的类型接收即可。而且支持重载!例如调用的是handleMessage,可以写两个同名方法handleMessage,参数分别是String和Map,会根据发送的消息是TextMessage或MapMessage来选择对应处理方法。

监听器:ConsumerMessageListenerAdapter

 

调试方法:TestDemo类testSend3方法。

实验总结:监听器不需要实现任何接口,不需要跟onMessage方法一样接收一个Message参数再自己转换,可以根据消息的类型选择对应的重载方法

 

 

实例4:MessageListenerAdapter绑定其他监听方法

配置请从applicableContext.xml中搜索“实例4”。

其中监听器是这样配置的:

<bean id="consumerMessageListenerAdapter2"
		class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <!-- 调用其他处理方法 -->
    <property name="delegate">
        <bean class="demo.jms.listener.ConsumerMessageListenerAdapter2" />
    </property>
    <property name="defaultListenerMethod" value="receiveMessage" />
</bean>

分别注入delegate属性监听的类,defaultListenerMethod属性监听处理方法。

监听器:ConsumerMessageListenerAdapter2

 

调试方法:TestDemo类testSend4方法。

实验总结:有时候需要绑定其他方法时可以使用。

 

 

实例5:MessageListenerAdapter进行回复

配置请从applicableContext.xml中搜索“实例5”。

其中监听器是这样配置的:

<bean id="consumerMessageListenerAdapter3"
		class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg>
        <bean class="demo.jms.listener.ConsumerMessageListenerAdapter3" />
    </constructor-arg>
    <!-- 回复的目的地 -->
    <property name="defaultResponseDestination" ref="responseQueue" />
</bean>

在defaultResponseDestination属性注入回复消息的目的地,然后监听调用的方法直接return就可以了。

监听器:ConsumerMessageListenerAdapter3

 

调试方法:TestDemo类testSend5方法。

实验总结:回复消息比SessionAwareMessageListener更简单直接。

 

 

实例6: 消息转换器MessageConverter

配置请从applicableContext.xml中搜索“实例6”。

jms的消息转换器是MessageConverter接口,写一个类实现MessageConverter接口的toMessage和fromMessage方法,还需要给JmsTemplate的bean中配置该转换器,实例6中的配置:

<bean id="jmsTemplate2" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <!-- 指定消息转化器 -->
    <property name="messageConverter" ref="emailMessageConverter" />
</bean>

当JmsTemplate配置了转换器,发送消息时调用convertAndSend方法,它会自动调用消息转换器的toMessage方法,监听器接收到消息后,需要手动调用消息监听器的fromMessage方法。如果不给JmsTemplate注入消息监听器,则convertAndSend会使用默认的转换器,和MessageListenerAdapter的自动转换功能一样。

本实例中发送的是一个Email对象,将Message中的邮件反序列化,调用objMessage.getObject()时,需要下面这段代码:

System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","*")

设置系统参数,参数值表示可被反序列化的包名,“*”表示所有包下的类都能被反序列化,否则会报javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker错误。目前我只想到写一个WebApplicationinitializer在spring初始化的时候设置一次这个变量,本项目代码没弄那么复杂直接在反序列化之前设置该参数

监听器:EmailQueueListener

 

调试方法:TestDemo类testSend6方法。

实验总结:发送消息方面,只不过将JmsTemplate的send方法的MessageCreator参数的匿名实现方法写在消息转换器的toMessage方法中,接收消息方面,调用fromMessage是自己手动调用的:在监听器里注入消息转换器,自己手动写代码调用。

 

 

实例7:MessageListenerAdapter绑定消息转换器

配置请从applicableContext.xml中搜索“实例7”。

实例7中的监听器配置:

<bean id="emailQueueAdapter"
		class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <!-- 默认调用方法handleMessage -->
    <constructor-arg>
        <bean class="demo.jms.listener.EmailQueueAdapter" />
    </constructor-arg>
    <!-- 消息转换器 -->
    <property name="messageConverter" ref="emailMessageConverter"/> 
</bean>

给属性messageConverter注入消息转化器,MessageListenerAdapter就不再使用默认的转换器,而是使用指定的进行转换,还可以给他设置空值,不做任何处理:

<property name="messageConverter">  
    <null/>  
</property>

 

监听器:EmailQueueAdapter

 

调试方法:TestDemo类testSend7方法。

实验总结:与实例6相同的是,发送消息调用JmsTemplate的convertAndSend方法,会默认调用消息转换器的toMessage方法,不同的是,EmailQueueListener接收到消息会默认调用消息转换器的fromMessage方法,这样种方式就解决MessageConverter的fromMessage方法手动调用的问题了

 

 

持久化到MySql

ActiveMQ接收到生产者的消息存放在内存中,所以很有必要把消息持久化到数据库里的。

打开ActiveMQ文件夹的/conf/activemq.xml中进行配置。

第一步首先配置一个数据库连接池的bean,看别人写的贴子里,就是一个dbcp连接池配置,而我下载的5.13版本会报找不到类的错误,因为ActiveMQ没有dbcp连接池的jar包,可能之前的版本是有自带dbcp,这个版本没有,所以得自己下载。我估计这样是为了让用户自己选连接池,所以我这里选的是c3p0,因为它支持数据库断开自动重连。

http://sourceforge.net/projects/c3p0/这里下载c3p0压缩包,取出lib文件夹里的jar包,还需要下载mysql的连接jdbc的那个驱动mysql-connector-java.jar,将这些jar放进ActiveMQ目录下的lib文件夹。

然后配置c3p0连接池,在</broker>标签下面添加这个spring的bean:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUrl" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true" />
    <property name="user" value="root" />
    <property name="password" value="123456" />
    <property name="initialPoolSize" value="5" />
    <property name="minPoolSize" value="5" />
    <property name="maxPoolSize" value="100" />
    <property name="maxIdleTime" value="600" />
    <property name="acquireIncrement" value="5" />
    <property name="checkoutTimeout" value="60000" />
</bean>

具体使用什么连接池,连接参数是什么可以根据自己喜好而定。

第二步:找到<persistenceAdapter>标签,原来的内容注释掉,改成如下内容:

<!--
<persistenceAdapter>
    <kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
-->
		
<persistenceAdapter>
    <!-- dataSource值为:“#连接池的id” -->
    <jdbcPersistenceAdapter dataSource="#dataSource"/>
</persistenceAdapter>

第三步:‍在mysql中建立一个叫“activemq”的数据库,就是连接池进行连接的那个数据库,名字可以自己取,只要c3p0能连上就行。重启ActiveMQ会自动在数据库中建立需要的表。

测试:将实例1的监听容器注释掉,使该组消息队列只能发送消息,不能监听到消息,执行TestDemo类的testSend方法,会看到数据库插入了还未处理的消息记录。

173949_IJs4_2000700.png

将监听容器取消注释,再次执行调试方法,控制台显示发送了2条消息,处理了4条消息,而之前数据库中的记录不见了。

174345_yFOU_2000700.png

补充:我用的是c3p0连接池是为了数据库断开重连问题,但是实际上不太可能让ActiveMQ远程连接数据库,这样效率太低,数据库和ActiveMQ应该放在一起。ActiveMQ接收消息并发送给监听器不停插入删除动作消耗性能,所以不建议数据库和业务系统一起使用。dbcp连接池的效率比c3p0高,所以推荐用dbcp。

 

后面想研究RabbitMQ了,ActiveMQ内容还有topic消息(本文是queue)、jms事物(实现jta),与zookeeper集群等读者可以自行百度研究,如果我有时间也会补充在本demo里。

 

转载于:https://my.oschina.net/js99st/blog/609417

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值