版本说明
Spring 4.3.16.RELEASE
activemq-all 5.15.3
activemq.properties 文件
在 activemq.properties
文件中配置ActiveMQ服务器的url, userName, password等. 还可以指定queue和topic的队列名.
# ActiveMQ Config
com.tao.activemq.brokerURL = tcp\://127.0.0.1\:61616
com.tao.activemq.userName = admin
com.tao.activemq.password = admin
com.tao.activemq.pool.maxConnection = 10
# queueName
com.tao.activemq.queueName = activemq_queue_01
#topicName
com.tao.activemq.topicName = activemq_topic_01
spring-activemq.xml 配置文件说明
在 spring-activemq.xml
配置文件中, 我们需要配置 ConnectionFactory
, JmsTemplate
, Destination
, Consumer的监听器
.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 1: 读入配置文件 -->
<context:property-placeholder location="classpath*:properties/activemq.properties" ignore-unresolvable="true"/>
<!-- 2: 扫描service包下的所有使用注解的类型 -->
<context:component-scan base-package="com.tao.smp.mq"/>
<!-- 3: 配置ConnectionFactory -->
<!-- 真正的由JMS服务器厂商提供的ConnectionFactory -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- ActiveMQ Config -->
<property name="brokerURL" value="${com.tao.activemq.brokerURL}"/>
<property name="userName" value="${com.tao.activemq.userName}"/>
<property name="password" value="${com.tao.activemq.password}"/>
</bean>
<!--
ActiveMQ为我们提供了一个PooledConnectionFactory, 通过往里面注入一个ActiveMQConnectionFactory
可以用来将Connection、Session和MessageProducer池化, 这样可以大大减少资源的消耗.
-->
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory" ref="targetConnectionFactory"/>
<property name="maxConnections" value="${com.tao.activemq.pool.maxConnection}"/>
</bean>
<!-- Spring用于管理connectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 注入真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="pooledConnectionFactory"/>
</bean>
<!-- 4: Spring JmsTemplate 的消息生产者Producer -->
<!-- 生产者bean -->
<bean id="queueProducer" class="com.tao.smp.mq.producer.queue.QueueProducer"/>
<bean id="topicProducer" class="com.tao.smp.mq.producer.topic.TopicProducer"/>
<!-- 定义JmsTemplate的 Queue 类型 -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
<!-- 非pub/sub模型(发布/订阅), 即队列模式 -->
<property name="pubSubDomain" value="false"/>
<!--接收超时时间-->
<!--<property name="receiveTimeout" value="10000" />-->
</bean>
<!-- 定义JmsTemplate的 Topic 类型 -->
<bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<constructor-arg ref="connectionFactory"/>
<!-- pub/sub模型(发布/订阅) -->
<property name="pubSubDomain" value="true"/>
<!--接收超时时间-->
<!--<property name="receiveTimeout" value="10000" />-->
</bean>
<!-- 5: 定义消息目的地destination -->
<!-- 支持点对点模式的queue -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${com.tao.activemq.queueName}"/>
</bean>
<!-- 支持发布/订阅模式的topic -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="${com.tao.activemq.topicName}"/>
</bean>
<!-- 6: 定义消息消费者consumer -->
<!-- 消费者bean -->
<bean id="queueConsumer1" class="com.tao.smp.mq.consumer.queue.QueueConsumer1"/>
<bean id="queueConsumer2" class="com.tao.smp.mq.consumer.queue.QueueConsumer2"/>
<bean id="topicConsumer1" class="com.tao.smp.mq.consumer.topic.TopicConsumer1"/>
<bean id="topicConsumer2" class="com.tao.smp.mq.consumer.topic.TopicConsumer2"/>
<!-- 定义 Queue 监听器 -->
<jms:listener-container destination-type="queue" container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<jms:listener destination="${com.tao.activemq.queueName}" ref="queueConsumer1"/>
<jms:listener destination="${com.tao.activemq.queueName}" ref="queueConsumer2"/>
</jms:listener-container>
<!-- 定义 Topic 监听器 -->
<jms:listener-container destination-type="topic" container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<jms:listener destination="${com.tao.activemq.topicName}" ref="topicConsumer1"/>
<jms:listener destination="${com.tao.activemq.topicName}" ref="topicConsumer2"/>
</jms:listener-container>
</beans>
Producer 和 Consumer
点对点模式(queue)
点对点模式, 生产者生产的消息只能被一个消费者消费.
生产者 QueueProducer.java
package com.tao.smp.mq.producer.queue;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
public class QueueProducer {
@Resource(name = "jmsQueueTemplate")
private JmsTemplate jmsQueueTemplate;
/**
* 发送点对点消息
* @param msg
*/
public void sendMsg(Destination destination, final String msg) {
System.out.println("QueueProducer发送消息: " + msg);
jmsQueueTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}
});
}
}
通过Spring提供JmsTemplate供生产者使用, 生产者注入JmsTemplate对象,然后通过它去发送消息.
JmsTemplate 的 Bean 我们在 spring-activemq.xml 配置文件中进行了配置. 在这里, 点对点模式使用的是 jmsQueueTemplate 对象.
<!-- 定义JmsTemplate的 Queue 类型 -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
<!-- 非pub/sub模型(发布/订阅), 即队列模式 -->
<property name="pubSubDomain" value="false"/>
<!--接收超时时间-->
<!--<property name="receiveTimeout" value="10000" />-->
</bean>
queueDestination
<!-- 支持点对点模式的queue -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${com.tao.activemq.queueName}"/>
</bean>
消费者 QueueConsumer1.java 和 QueueConsumer2.java
package com.tao.smp.mq.consumer.queue;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class QueueConsumer1 implements MessageListener {
@Override
public void onMessage(Message message) {
// 接收到消息, 进行类型转换
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("QueueConsumer1接收到消息: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
package com.tao.smp.mq.consumer.queue;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class QueueConsumer2 implements MessageListener {
@Override
public void onMessage(Message message) {
// 接收到消息, 进行类型转换
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("QueueConsumer2接收到消息: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
这里的消费者实现 MessageListener
接口, 用于监听MQ上的消息.
测试 QueueProducerTest.java
BaseTest.java
package com.tao.smp.mq.consumer.queue;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:config/spring-config-all.xml")
public class BaseTest {
}
spring-config-all.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- Spring引入各个模块的配置文件 -->
<import resource="classpath*:config/spring-mybatis.xml"/>
<import resource="classpath*:config/spring-service.xml"/>
<import resource="classpath*:config/spring-web.xml"/>
<import resource="classpath*:config/spring-task.xml"/>
<import resource="classpath*:config/spring-interceptor.xml"/>
<import resource="classpath*:config/spring-activemq.xml"/>
</beans>
QueueProducerTest.java
package com.tao.smp.mq.producer.queue;
import com.tao.smp.mq.consumer.queue.BaseTest;
import org.apache.activemq.command.ActiveMQQueue;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
public class QueueProducerTest extends BaseTest {
@Autowired
private QueueProducer queueProducer;
@Resource(name = "queueDestination")
private ActiveMQQueue queueDestination;
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
* Method: sendMsg(Destination destination, final String msg)
*/
@Test
public void testSendMsg() throws Exception {
while(true) {
queueProducer.sendMsg(queueDestination, "queue测试消息,哈哈~~");
System.out.println("======sleep 4 sec======");
TimeUnit.SECONDS.sleep(4);
}
}
}
测试结果:
Pub/Sub模式(topic)
Pub/Sub模式下, 一个生产者生产的一个消息会被所有注册的消费者接收.
生产者 TopicProducer.java
package com.tao.smp.mq.producer.topic;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
public class TopicProducer {
@Resource(name = "jmsTopicTemplate")
private JmsTemplate jmsTopicTemplate;
public void sendMsg(Destination destination, final String msg) {
System.out.println("TopicProducer发送消息: " + msg);
jmsTopicTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}
});
}
}
注意这里我们注入的是 jmsTopicTemplate
, 在 spring-activemq.xml
配置文件中配置如下:
<!-- 定义JmsTemplate的 Topic 类型 -->
<bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<constructor-arg ref="connectionFactory"/>
<!-- pub/sub模型(发布/订阅) -->
<property name="pubSubDomain" value="true"/>
<!--接收超时时间-->
<!--<property name="receiveTimeout" value="10000" />-->
</bean>
topicDestination
<!-- 支持发布/订阅模式的topic -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="${com.tao.activemq.topicName}"/>
</bean>
消费者 TopicConsumer1.java 和 TopicConsumer2.java
package com.tao.smp.mq.consumer.topic;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class TopicConsumer1 implements MessageListener {
@Override
public void onMessage(Message message) {
// 接收到消息, 进行类型转换
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("TopicConsumer1接收到消息: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
package com.tao.smp.mq.consumer.topic;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class TopicConsumer2 implements MessageListener {
@Override
public void onMessage(Message message) {
// 接收到消息, 进行类型转换
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("TopicConsumer2接收到消息: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
测试类 TopicProducerTest.java
package com.tao.smp.mq.producer.topic;
import com.tao.smp.mq.consumer.queue.BaseTest;
import org.apache.activemq.command.ActiveMQTopic;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
public class TopicProducerTest extends BaseTest {
@Autowired
private TopicProducer topicProducer;
@Resource(name = "topicDestination")
private ActiveMQTopic topicDestination;
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
* Method: sendMsg(Destination destination, final String msg)
*/
@Test
public void testSendMsg() throws Exception {
while(true) {
topicProducer.sendMsg(topicDestination, "topic测试信息, 试试~~");
System.out.println("======sleep 4 sec======");
TimeUnit.SECONDS.sleep(4);
}
}
}
测试结果