Spring+JMS+ActiveMQ基础案例

版本说明

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);
        }
    }
} 

测试结果
这里写图片描述

猜你喜欢

转载自blog.csdn.net/hbtj_1216/article/details/80600672