ActiveMQ
一、Spring整合ActiveMQ
我个人的理解:我们之前介绍的内容也很重要,他更灵活,他支持各种自定义功能,可以满足我们工作中复杂的需求。很多activemq的功能,我们要看官方文档或者博客,这些功能大多是在上面代码的基础上修改完善的。如果非要把这些功能强行整合到spring,就有些缘木求鱼了。我认为另一种方式整合spring更好,就是将上面的类注入到Spring中,其他不变。这样既能保持原生的代码,又能集成到spring。
不过将的Spring和SpringBoot整合ActiveMQ也重要,他给我们提供了一个模板,简化了代码,减少我们工作中遇到坑,能够满足开发中90%以上的功能。
1、pom.xml添加依赖
<dependencies>
<!-- activemq 所需要的jar 包-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- 嵌入式activemq的broker所需要的依赖包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- activemq连接池 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.9</version>
</dependency>
<!-- spring支持jms的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<!--spring相关依赖包-->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<!--junit/log4j等基础通用配置-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2、Spring的ActiveMQ配置文件
<?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">
<!--包扫描-->
<context:component-scan base-package="com.achang.spring"/>
<!--配置连接池工厂-->
<bean id="jsmFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<!--真正可以产生Connection的ConnectionFactory,由对应的JMS服务厂商提供-->
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.109.101:61616"/>
</bean>
</property>
<!--最大连接数-->
<property name="maxConnections" value="100"/>
</bean>
<!--队列目的地,点对点的-->
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<!--Spring提供的JMS工具类,它可以进行消息发送、接收等-->
<bean class="org.springframework.jms.core.JmsTemplate" id="jmsTemplate">
<!--引用哪个工厂-->
<property name="connectionFactory" ref="jsmFactory"/>
<!--使用哪个目的地,主题、队列-->
<property name="defaultDestination" ref="destinationQueue"/>
<!--消息类型转换、类型强转和底层的适配-->
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
</property>
</bean>
</beans>
阿昌遇到的问题:
遇到了【org.springframework.beans.factory.NoSuchBeanDefinitionException】的报错
根据网上的资料发现都没用
自己在最后检查配置文件,发现 JmsTemplate的全类名引入错误,引入了MessageJmsTemplate,和id都错了,所有才会找不到Bean报这个异常
3、【队列Queue】生产者
@Service()
public class SpringMQ_Producer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Producer producer = (SpringMQ_Producer) ioc.getBean("springMQ_Producer");
// producer.jmsTemplate.send(new MessageCreator() {
// @Override
// public Message createMessage(Session session) throws JMSException {
// TextMessage textMessage = session.createTextMessage("。。。Spring和ActiveMQ的整合case。。。");
// return textMessage;
// }
// });
producer.jmsTemplate.send(session -> {
TextMessage textMessage = session.createTextMessage("Spring和ActiveMQ的整合case。。。。");
return textMessage;
});
System.out.println("。。。。。。。send() over。。。。。。");
}
}
4、【队列Queue】消费者
@Service
public class SpringMQ_Consumer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) throws JMSException {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Consumer consumer = (SpringMQ_Consumer) ioc.getBean("springMQ_Consumer");
String returnValue = (String) consumer.jmsTemplate.receiveAndConvert();
System.out.println("。。。消费者收到的消息: " + returnValue);
}
}
5、【主题Topic】生产者&消费者
给容器注入一个主题的Bean对象,然后在JmsTemplate的目的地引用改为主题Bean对象的id引用即可
注意:
因为是主题目的地,所以要先启动消费者,再启动生产者
阿昌遇到的问题:

因为是主题目的地,需要先打开消费者监听,我启动消费者报错:【Cannot use a Temporary destination from another Connection; nested exception is javax.jms.InvalidDestinationException: Cannot use a Temporary destination from another Connection】
经过后面的检查↓
其中引入的class路径出现的问题,应该导入的包不是
"org.apache.activemq.command.ActiveMQTempTopic"
,而是"org.apache.activemq.command.ActiveMQTopic"
不需要修改之前的代码
6、【主题Topic】配置消费者的监听类
配置文件:
注意:
destination的ref引用的确定是自己需要的目的地,不然监听器会监听不对
监听器:
@Component
public class MyMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if (null!=message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
String text = null;
try {
text = textMessage.getText();
} catch (JMSException e) {
e.printStackTrace();
}
System.out.println("消费者监听器监听的资源为: " + text);
}
}
}
配置了监听器后,就不需要启动消费者。
消费者自动监听生产者
二、SpringBoot整合ActiveMQ
1、队列
1)队列生产者
(1)新建项目
(2)pom.xml
<?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 https://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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.achang</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</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>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
(3) application.yml
server:
port:7777
spring:
activemq:
broker-url: tcp://192.168.109.101:61616 # 自己的MQ服务器地址
user: admin
password: admin #指定MQ服务器的账号密码
jms:
pub-sub-domain: false # false = Queue ; true = Topic 【默认值 false 】
#自定义队列名称
myqueue: boot-activemq-queue
(4)配置目的地的bean
配置目的地的bean和开启springboot的jms功能
@EnableJms
: 开启jms适配
@Component// 让spring管理的注解,相当于spring中在xml 中写了个bean
@EnableJms// 开启jms适配
public class ConfigBean {
// 注入配置文件中的 myqueue
@Value("${myqueue}")
private String myQueue;
@Bean // bean id="" class="…"
public Queue queue(){
return new ActiveMQQueue(myQueue);
}
}
(5)队列生产者代码
队列生产者代码。发送消息
package com.achang.boot.activemq.produce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.UUID;
/******
@author 阿昌
@create 2021-01-26 15:08
*******
*/
@Component
public class Queue_Produce {
// JMS模板
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
// 这个是我们配置的队列目的地
@Autowired
private Queue queue;
// 发送消息
public void produceMsg(){
// 一参是目的地,二参是消息的内容
jmsMessagingTemplate.convertAndSend(queue,"*****"+ UUID.randomUUID().toString().substring(1,5));
}
//间隔时间3秒钟定投
@Scheduled(fixedDelay = 3000)
public void produceMsgScheduled(){
produceMsg();
System.out.println("******* produceMsgScheduled send ok");
}
}
(6)主启动类
@EnableScheduling
:开启定期投送功能
package com.achang.boot.activemq.produce;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/******
@author 阿昌
@create 2021-01-26 15:12
*******
*/
@SpringBootApplication
@EnableScheduling //开启定期投送功能
public class MainApp_Produce {
public static void main(String[] args) {
SpringApplication.run(MainApp_Produce.class,args);
}
}
(7)单元测试
@SpringBootTest
public class TestActiveMQ {
@Autowired
private Queue_Produce queue_produce;
@Test
void testSend(){
queue_produce.produceMsg();
}
@Test
void testSend() throws IOException {
queue_produce.produceMsgScheduled();
System.in.read();
}
}
效果:
定时投送的测试:每三秒投送一次
2)队列消费者
pom.xml和application.yml文件和前面一样。唯一不同就是下面代码
注册一个消息监听器。项目开启后监听某个主题的消息。
package com.achang.boot.activemq.consumer;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.TextMessage;
/******
@author 阿昌
@create 2021-01-26 16:20
*******
*/
@Component
public class Queue_Consumer {
//告知监听哪个队列
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage) throws JMSException {
System.out.println("====消费者收到消息====: "+ textMessage.getText());
}
}
主启动类:
@SpringBootApplication
@EnableScheduling
public class MainApp_Consumer {
public static void main(String[] args) {
SpringApplication.run(MainApp_Consumer.class,args);
}
}
注意:
在配置文件中记得修改这个微服务的端口号避免与上面的生产者端口号发生冲突
server:
port:7778 # 修改端口号避免与生产者端口发生冲突
spring:
activemq:
broker-url: tcp://192.168.109.101:61616 # 自己的MQ服务器地址
user: admin
password: admin #指定MQ服务器的账号密码
jms:
pub-sub-domain: false # false = Queue ; true = Topic 【默认值 false 】
#自定义队列名称
myqueue: boot-activemq-queue
效果:
2、发布订阅
1)topic生产者
(1)pom.xml
和上面一样。
(2)application.yml
server:
port:7779
spring:
activemq:
broker-url: tcp://192.168.109.101:61616 # 自己的MQ服务器地址
user: admin
password: admin #指定MQ服务器的账号密码
jms:
pub-sub-domain: true # false = Queue ; true = Topic 【默认值 false 】
#自定义队列名称
mytopic: boot-activemq-topic
(3) 配置目的地的bean和开启JMS功能
@Component
@EnableJms
public class ConfigBean {
@Value("${mytopic}")
private String mytopic;
@Bean
public Topic topic() {
return new ActiveMQTopic(mytopic);
}
}
(4)生产者代码
@Component
public class Topic_Produce {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic topic;
public void produceTopicMsg() {
jmsMessagingTemplate.convertAndSend(topic, "***主题消息*****" + UUID.randomUUID().toString().substring(1, 5));
}
//间隔时间3秒钟定投
@Scheduled(fixedDelay = 3000)
public void produceTopicMsgScheduled() {
produceTopicMsg();
System.out.println("******* produceMsgScheduled send ok");
}
}
2)topic消费者
(1) pom.xml
和上面一样。
(2)application.yml
和上面一样。
(3)消费者代码
@Component
public class Topic_Consumer {
@JmsListener(destination = "${mytopic}")
public void receive(TextMessage textMessage)throws JMSException {
System.out.println("消费者收到订阅的主题消息: " + textMessage.getText());
}
}
(4)主启动类
两个启动类,启动前,修改配置类中的端口一个为7780、一个为7781
@SpringBootApplication
public class MainApp7780 {
public static void main(String[] args) {
SpringApplication.run(MainApp7780.class,args);
}
}
@SpringBootApplication
public class MainApp7781 {
public static void main(String[] args) {
SpringApplication.run(MainApp7781.class,args);
}
}
测试:
需要先启动消费者订阅后,再启动生产者
感谢尚硅谷!感谢阳哥!