Java配置方式使用AMQ
1. 引入依赖(pom.xml)
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<groupId>org.hik.hyy</groupId>
<artifactId>homework_boot</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>homework_boot Maven Webapp</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot操作数据库依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- freemarker模板引擎视图 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- jsp解析器 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入StringUtils工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- 切面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<!-- 日志包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<!-- JMS-Activemq -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.2</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 资源文件拷贝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- maven启动插件 spring-boot:run -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2. 编写配置类(JMSConfig、AMQConfig)
萌新刚开始使用springboot,我在测试的使用使用两个test方法加载同一个SpringApplication容器,又因为MessageListenerContainer又写在了这个配置类中,导致接收方法接收消息的时候发现消息“丢失”了(发送方法的test也接收到了消息),所以这里写两个配置类,内容起始都是一样的,只不过让发送的方法、接收方法加载两个不同的容器。
2.1JMSConfig(发送方法加载)
package com.hik.hyy.config;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
@SpringBootConfiguration
@PropertySource(value = {"classpath:db.properties"}, ignoreResourceNotFound = true)
public class JmsConfig {
@Value("${brokerURL}")
private String brokerURL;
@Value("${queue.name}")
private String queue;
@Value("${topic.name}")
private String topic;
//真正可以产生Connection的ConnectionFactory,即ActiveMq创建的
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory(){
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerURL);
return activeMQConnectionFactory;
}
//配置Spring管理的ConnectionFactory
@Bean
public SingleConnectionFactory singleConnectionFactory(){
//目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory
SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory(activeMQConnectionFactory());
return singleConnectionFactory;
}
//Spring提供的JMS模板类,用于消息发送、接收等
@Bean
public JmsTemplate jmsTemplate(){
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(singleConnectionFactory());
return jmsTemplate;
}
//这个是queue目的地,点对点的
@Bean
public ActiveMQQueue queueDestination(){
ActiveMQQueue activeMQQueue = new ActiveMQQueue(queue);
return activeMQQueue;
}
//这个是topic目的地,一对多的
@Bean
public ActiveMQTopic topicDestination(){
ActiveMQTopic activeMQTopic = new ActiveMQTopic(topic);
return activeMQTopic;
}
}
db.properties(里面配置数据库等信息,懒癌比较严重所以就写里面了)
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/homework?characterEncoding=UTF-8&useSSL=false
jdbc.username=root
jdbc.password=root
#ActiveMq
brokerURL=tcp://10.20.81.118:61616
queue.name=hyy-queue
topic.name=hyy-topic
2.2AMQConfig(接收方法加载的)
先编写接收消息的监听类MyMessageListener
package com.hik.hyy.jms.mvcMQ;
import java.util.Optional;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class MyMessageListener implements MessageListener{
@Override
public void onMessage(Message message) {
try {
//练习下Optional的使用
Optional<TextMessage> textOpt = Optional.ofNullable((TextMessage)message);
if (textOpt.isPresent()) {
System.err.println(textOpt.get().getText());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接着编写配置类AMQConfig
package com.hik.hyy.config;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
@SpringBootConfiguration
@PropertySource(value = {"classpath:db.properties"}, ignoreResourceNotFound = true)
public class JmsConfig {
@Value("${brokerURL}")
private String brokerURL;
@Value("${queue.name}")
private String queue;
@Value("${topic.name}")
private String topic;
//真正可以产生Connection的ConnectionFactory,即ActiveMq创建的
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory(){
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerURL);
return activeMQConnectionFactory;
}
//配置Spring管理的ConnectionFactory
@Bean
public SingleConnectionFactory singleConnectionFactory(){
//目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory
SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory(activeMQConnectionFactory());
return singleConnectionFactory;
}
//Spring提供的JMS模板类,用于消息发送、接收等
@Bean
public JmsTemplate jmsTemplate(){
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(singleConnectionFactory());
return jmsTemplate;
}
//这个是queue目的地,点对点的
@Bean
public ActiveMQQueue queueDestination(){
ActiveMQQueue activeMQQueue = new ActiveMQQueue(queue);
return activeMQQueue;
}
//这个是topic目的地,一对多的
@Bean
public ActiveMQTopic topicDestination(){
ActiveMQTopic activeMQTopic = new ActiveMQTopic(topic);
return activeMQTopic;
}
//注册MessageListener
@Bean
public MyMessageListener myMessageListener(){
return new MyMessageListener();
}
//消息监听器
@Bean
public DefaultMessageListenerContainer defaultMessageListenerContainer(){
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(singleConnectionFactory());
//监听queue
container.setDestination(queueDestination());
//监听topic
// container.setDestination(topicDestination());
container.setMessageListener(myMessageListener());
return container;
}
}
这里说明下,如果要监听topic的话,container.setMessageListener()方法里面的对象切换下。
3. 编写测试方法
package com.hik.hyy.jms.mvcMQ;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hik.hyy.config.AMQConfig;
import com.hik.hyy.config.JmsConfig;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JmsConfig.class}) //必须要有,不然Failed to load ApplicationContext
//@ContextConfiguration(classes = {JmsConfig.class,AMQConfig.class}) 这样配置会导致发消息的时候,自己也能监听到
public class ActiveMQTest {
//加载模板类
@Autowired
private JmsTemplate jmsTemplate;
private ApplicationContext applicationContext;
/**
* @Description: 队列消息生产者
* @param @throws Exception
* @return void
* @date: 2018年9月26日 下午4:22:43
* @throws
*/
@Test
public void testQueueProducer() throws Exception{
//加载生产者的配置类
applicationContext = new AnnotationConfigApplicationContext(JmsConfig.class);
Queue queue = (Queue) applicationContext.getBean("queueDestination");
for (int i = 0; i < 5; i++) { //生产五条消息
jmsTemplate.send(queue, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello,this is a queueMessage");
return message;
}
});
}
}
/**
* @Description: 主题消息生产者
* @param @throws Exception
* @return void
* @date: 2018年9月26日 下午4:23:13
* @throws
*/
@Test
public void testTopicProducer() throws Exception{
//加载生产者的配置类
applicationContext = new AnnotationConfigApplicationContext(JmsConfig.class);
Topic topic = (Topic) applicationContext.getBean("topicDestination");
for (int i = 0; i < 5; i++) { //生产5条消息
jmsTemplate.send(topic, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello,this is a topicMessage");
return message;
}
});
}
}
/**
* @Description: 消费者
* @param @throws Exception
* @return void
* @author: huyuyuan
* @date: 2018年9月26日 下午4:24:02
* @throws
*/
@Test
public void testConsumer() throws Exception{
//这里加载消费配置类
applicationContext = new AnnotationConfigApplicationContext(AMQConfig.class);
System.in.read();
}
}
4. 结果
4.1queue
运行testQueueProducer()方法:
再运行testConsumer()方法
这也说明在点对点模式中,消费者和生产者并没有时间上的关联,并且是一对一的消费,具体的话,将生产者加载的配置类改成跟消费者一样, 最后会发现生产者和消费者受到的消息总数跟发送者发出的是相等的(之前我以为是消息丢失的缘故)
4.2 topic
为了体现多个订阅者都能监听到消息,我们将生产者加载的配置类改成跟消费者一样的配置:
@Test
public void testTopicProducer() throws Exception{
//加载生产者的配置类
//加载消费者配置类,验证一个主题消息可以被多个消费者消费
applicationContext = new AnnotationConfigApplicationContext(AMQConfig.class);
Topic topic = (Topic) applicationContext.getBean("topicDestination");
for (int i = 0; i < 5; i++) { //生产5条消息
jmsTemplate.send(topic, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello,this is a topicMessage");
return message;
}
});
}
}
运行testConsumer()方法启动监听,再运行testTopicProducer()发送消息,
反之,如果先运行生产者方法,生产者能够接收到消息,但消费者就接收不到了消息了