消息中间件入门「二」:在spring中使用activemq

消息中间件入门「二」:在spring中使用activemq

在普通的java项目使用activemq一般需要经过:1.获得连接工厂;2.创建连接;2.启动连接;3.获取会话(session);4.绑定连接地址(detination)5.获得消费者/生产者;6.生产/消费消息。
而在spring中使用activemq则可以将上述一系列的步骤写入spring的xml文件中,拓展性和复用性更好

项目结构

项目结构
【非常简单有木有】

依赖jar包

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-spring</artifactId>
        <version>5.15.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>

spring配置文件

<?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"
       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">
    <!--注入自定的消费者监听器bean和生产者,便于在app中测试-->
    <context:component-scan base-package="queue"/>
    <!--activeMq的连接工厂配置-->
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616" />
        <property name="trustAllPackages" value="true"/>
    </bean>
    <!--整合到spring的连接工厂中-->
    <bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
        <property name="sessionCacheSize" value="100"/>
    </bean>
    <!--生产者发送消息的bean,相当于之前生产者绑定destination和send方法都集成在这个bean里面-->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"></property>
    </bean>
    <!--队列-->
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="queue"/>
    </bean>
    <!--主题-->
    <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg value="topic"/>
    </bean>
<!--现在监听器由spring托管,这部分相当于之前消费者的监听器部分代码-->
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="queueDestination"/>
        <property name="messageListener" ref="messageListener"/>
    </bean>
</beans>

说明:
1.trustAllPackages设置为true表示信任所有,可以为所欲为的使用objectMessage传输消息。
2.CachingConnectionFactory不一定非要这个连接工厂,在org.springframework.jms.connection下有很多连接工厂,具体看官方api。
3.jmsTemplate相当于整合了生产者建立连接的过程,在代码中发送Message,只需从IOC中取出这个bean,将发送的destinationMessage传入send方法中即可。
4.注入了两个destination的bean,一个是队列模式,一个是主题模式方便生产者和消费者绑定
5.由于消费者只需要确定接收消息的细节,即消息处理器[messageListener],因此需要在代码中书写接收到消息后的细节,并将此bean注入到IOC中,而整个监听器是由spring托管的,因此放在了org.springframework.jms.listener.DefaultMessageListenerContainer中,这了类里面实现了创建一个消费者具体处理逻辑,只需要将连接工厂[connectionFactory],监听地址[queueDestination/topicDestination],和消息处理器[messageListener]传入即可。其中监听地址根据队列/主题模式传入对应的Destination。

消费者处理器代码:

package queue;

import org.springframework.stereotype.Component;

import javax.jms.*;

@Component("messageListener")
public class QueueConstumer implements MessageListener {
    public void onMessage(Message message) {
        ObjectMessage Message= (ObjectMessage) message;
        try {
            System.out.println("收到消息:"+Message.getObject());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里的"messageListener"与xml配置文件中的jmsContainerbean的messageListener属性value是对应的,并且生产者传入的是什么Message类型消费取出时也必须要用与之对应的类型

生产者代码:

package queue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
import pojo.User;

import javax.annotation.Resource;
import javax.jms.*;

@Component
public class QueueProducer {
    @Autowired
    JmsTemplate jmsTemplate;
    @Resource(name = "queueDestination")
    Destination destination;
    public  void  sendMessage(final User msg){
        jmsTemplate.send(destination, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                final ObjectMessage objectMessage = session.createObjectMessage(msg);
                return objectMessage;
            }
        });
        System.out.println("发送消息:"+msg);
    }
}

解释:JmsTemplate是前面spring配置文件中注入进去的,用于生产者发送Message;而注解 @Resource(name = "queueDestination")后面带参数是因为在配置文件中定义的destination都属于jms.Destination的实现。完全可以写成ActiveMQQueue/ActiveMQTopic即可不用带@resource中的参数;发送消息指定调用JmsTemplate的send方法将Destination和Message传入即可。若是切换模式[topic/queue],则只需要对应传入不同的destination即可。

主程序APP代码:

import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.User;
import queue.QueueProducer;

import java.util.UUID;

public class App {
    public static void main(String[] args) throws InterruptedException {
        ClassPathXmlApplicationContext applicationContext= new ClassPathXmlApplicationContext("activeMq-config.xml");
        QueueProducer producer=applicationContext.getBean(QueueProducer.class);
        User user=new User();
        for (int i = 0; i < 100; i++) {
            user.setAge(i);
            user.setName((i%2==0? "Mr":"Mrs")+String.valueOf((char)(int)(Math.random()*26+'A'+1))+String.valueOf((char)(int)(Math.random()*26+'a')));
            user.setSex(i%2==0? "男":"女");
            user.setSalary((int)(Math.random()*10000));
            user.setNumber(UUID.randomUUID().toString());
            producer.sendMessage(user);
            Thread.sleep(50);
        }
        applicationContext.close();
    }
}

输出结果:

收到消息:User{name='MrNk', age=0, sex='男', number='b8fede00-2385-4b36-b70e-0b98dad260e2', salary=655}
发送消息:User{name='MrNk', age=0, sex='男', number='b8fede00-2385-4b36-b70e-0b98dad260e2', salary=655}
收到消息:User{name='MrsJm', age=1, sex='女', number='b3036d44-c7b0-4dbb-9ec2-de9b78e043d4', salary=5531}
发送消息:User{name='MrsJm', age=1, sex='女', number='b3036d44-c7b0-4dbb-9ec2-de9b78e043d4', salary=5531}
收到消息:User{name='MrXs', age=2, sex='男', number='68a2444a-8992-49b0-a9bd-cf00fcfb8763', salary=4556}
发送消息:User{name='MrXs', age=2, sex='男', number='68a2444a-8992-49b0-a9bd-cf00fcfb8763', salary=4556}
收到消息:User{name='MrsGf', age=3, sex='女', number='be4f0533-6721-49df-8174-e5cff5b1e24e', salary=2123}
发送消息:User{name='MrsGf', age=3, sex='女', number='be4f0533-6721-49df-8174-e5cff5b1e24e', salary=2123}
收到消息:User{name='MrBj', age=4, sex='男', number='958cdfa3-b97f-439a-b51c-30855b26d506', salary=5288}

可以看出由于都在一个项目中,消费者和生产者同时运行,因此一旦生产一条消息,立即被消费者取出。

主题/队列模式的切换:

由于spring简化了代码,因此切换模式也只需要修改两个地方即可:


  1. 队列模式

先修改配置中消费者的监听器配置:

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

只需要将destination的ref指向队列下的地址:queueDestination

再修改生产者的@resource注解的name值为队列下的:queueDestination
@Resource(name = "queueDestination")


  1. 主题模式

主题模式也一样,将生产者和消费者的detionation对应到:topicDestination即可:

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

修改@resource注解的参数为:topicDestination
@Resource(name = "topicDestination")

猜你喜欢

转载自blog.csdn.net/qq_24874939/article/details/82666195
今日推荐