- 通过Spring以及Spring Boot使得Active MQ的发送以及接收更加简单。更多的对象通过自动配置、自动注入的方式进入到IOC容器中,而我们只需要在容器中加入一些我们个性化定制的内容,比如说要发送的消息、监听器的行为等,就可以让实现自己的想要的功能。
1. Spring整合Active MQ
1.1 引入依赖
<!-- activemq核心依赖包 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<!-- activemq连接池 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.9</version>
</dependency>
<!--spring相关依赖包-->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>4.15</version>
</dependency>
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
1.2 Spring的配置文件
- 开启组件扫描
<context:component-scan base-package="ltd.dujiabao.activemq.spring.*"/>
- 配置
ConnectionFactory
将它放进一个池子里面<!-- 相当于拿到一个ConnectionFactory,还放进了一个池里面 --> <bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop"> <property name="connectionFactory"> <bean class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://192.168.233.134:61616"/> </bean> </property> </bean>
- 自己定制一个目的地,可以是queue,也可以是topic。并为该目的地定义一个名称。
<!-- 实例化一个queue的目的地,并命名为`spring-active-queue` --> <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg index="0" value="spring-active-queue"/> </bean> <!-- 实例化一个topic的目的地,并命名为`spring-active-topic` --> <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic"> <constructor-arg index="0" value="spring-active-topic"/> </bean>
- 配置一个
JmsTemplate
,它将创建Producer、Consumer、Message的工作、发送消息、注入目的地、接受并转换消息的工作封装在里面。对外留一个定义Message的接口。简化了发送消息、接受消息的流程。<!-- 类似于一个生产者或者消费者的角色 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory"/> <!-- <property name="defaultDestination" ref="destinationQueue"/>--> <property name="defaultDestination" ref="destinationTopic"/> <property name="messageConverter"> <bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/> </property> </bean>
- 至此,简单的配置已完成。
1.3 生产者
- 很简单,直接调用
jmsTemplate
的send
方法即可。 - 因此,首先自动注入
jmsTemplate
,然后自己写一个方法调用它的send方法 - 总的来说,这个生产者的代码,无关目的地类型,因为配置目的地的工作在配置文件中完成。
@Service public class ActiveMQProducer { @Autowired private JmsTemplate jmsTemplate; /** * 也就是说, 只要生产一个消息就好,其他的事,不用自己管。 * * @param text */ public void sendText(String text) { jmsTemplate.send((session) -> session.createTextMessage(text)); System.out.println("发送消息:" + text + " 成功"); } }
其中,send方法需要传入一个
MessageCreator
的实现类,也就是将内容返回到一个通过Session
创建的Message
中,这里通过简单的lambda表达式实现。 MessageCreator
@FunctionalInterface public interface MessageCreator { Message createMessage(Session var1) throws JMSException; }
1.4 消费者
- 消费者和上面的用法基本一致,通过JmsTemplate,接收Message里面的内容。JmsTemplate 会提前将Message的消息体转换出来。
@Service public class ActiveMQConsumer { @Autowired JmsTemplate jmsTemplate; public String recieveText() { Object o = jmsTemplate.receiveAndConvert(); // 接收并转换 if (o instanceof String) { String text = (String) o; System.out.println("收到消息: " + text); return text; } else return null; } }
- 当然,也可以配置一个监听器,使得接收到信息之后,自动触发。
@Component public class MyMessageListener implements MessageListener { @Override public void onMessage(Message message) { if (message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; try { System.out.println("收到了一条TextMessage" + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } } }
<!-- 配置一个监听器 --> <bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="destinationTopic"/> <property name="messageListener" ref="myMessageListener"/> </bean>
1.5 队列和主题
- 对于队列和主题,只需要简单的在配置文件中修改目的地,以及目的地注入的对象属性,就可以修改。代码方面没有太多需要修改的地方。
2. Spring Boot整合Active MQ
- 其实跟刚刚的用法一致,只是进一步简略了。
2.1 pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>mq-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot整合activemq的jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2 配置文件
- 配置文件主要配置MQ的地址端口号。以及目的地的类型。最后自定义了两个名称,在java文件中可以取出来,作为目的地的名称。
# web占用的端口 server: port: 7777 spring: activemq: # activemq的broker的url broker-url: tcp://192.168.233.134:61616 # 连接activemq的broker所需的账号和密码 user: admin password: admin jms: # 目的地是queue还是topic, false(默认) = queue true = topic # pub-sub-domain: true pub-sub-domain: true # 自定义队列名称。这只是个常量 myqueue: boot-activemq-queue clocktopic: boot-ctivemq-clock-queue
2.3 使用
- 在Spring Boot中,大部分配置都不需要了,只需要个性化配置一个自己的目的地即可。其中,在设置目的地名字的时候,就通过
@Value
注解将配置文件的内容注入。至此,目的地已进入IOC容器中。@Configuration @EnableJms // 允许使用JMS @EnableScheduling // 允许开启定时任务调度 public class ConfigBean { @Value("${myqueue}") private String myQueueName; @Value("${clocktopic}") private String clockTopicName; @Bean public Destination destination() { return new ActiveMQQueue(myQueueName);// 目的地为队列 //return new ActiveMQTopic(myQueueName); // 目的地为主题 } @Bean public Topic clockTopic() { return new ActiveMQTopic(clockTopicName); } }
- 生产者代码。在Spring的时候,使用了
JmsTemplate
,需要实例化MessateCreator
,还需要指定生成什么样的Message。而如今,使用JmsMessagingTemplate
就更加方便了。实际上,JmsMessagingTemplate
内部有一个属性就是JmsTemplate
,在内部它可以判断传入的Object类型,从而创建相对应的Message类型。@Service public class ActiveMQProducer { @Autowired JmsMessagingTemplate jmsMessagingTemplate; @Qualifier(value = "destination") @Autowired Destination destination; @Qualifier(value = "clockTopic") @Autowired Topic clockTopic; public void produceMessage(Object obj) { jmsMessagingTemplate.convertAndSend(destination, obj); } /** * 一个时钟定时器,发送消息 */ @Scheduled(fixedDelay = 1000) public void produceMessageScheduled() { jmsMessagingTemplate.convertAndSend(clockTopic, LocalTime.now().toString()); } }
- 消费者代码。如今写监听器就更加简单了,直接在方法上面标注一个
@JmsListener
注解,就可以注册某个名称的目的地的监听器,从而在消息进入之后,自动匹配方法的入参。@Slf4j @Component public class ActiveMQConsumer { ///** // * 写了一个TextMessage监听器 // * // * @param textMessage // * @throws JMSException // */ //@JmsListener(destination = "${myqueue}") //public void receiveText(TextMessage textMessage) throws JMSException { // String text = textMessage.getText(); // log.info("接收到TextMessage信息:" + text); //} @JmsListener(destination = "${myqueue}") public void receiveText(Message message) throws JMSException { if (message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; log.info("接收到一个TextMessage:" + textMessage.toString()); } if (message instanceof MapMessage) { MapMessage mapMessage = (MapMessage) message; log.info("接收到一个MapMessage:" + mapMessage.toString()); } } @JmsListener(destination = "${clocktopic}") public void receiveTime(TextMessage textMessage) throws JMSException { log.info("当前时间是::" + textMessage.getText()); } }
这种方法,就很像处理请求的注解
@RequestMapping
- 写一个定时定间隔发送消息的生产者,首先需要在配置类上面加一个注解
然后在在方法上面添加注解,表示时间间隔(毫秒级别)@EnableScheduling // 允许开启定时任务调度
@Scheduled(fixedDelay = 1000) public void produceMessageScheduled() { jmsMessagingTemplate.convertAndSend(clockTopic, LocalTime.now().toString()); //jmsTemplate.send(clockTopic, (session) -> session.createTextMessage(LocalTime.now().toString())); }