目录结构
代码
代码用Junit测试,省略web.xml和tomcat相关的配置
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>4.6</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
</dependencies>
config.properties
#参考http://activemq.apache.org/failover-transport-reference.html
mq.url=failover:(tcp://127.0.0.1:61616,tcp://127.0.0.2:61616)?randomize=false
#用户名
mq.username=admin
#密码
mq.password=admin
#queue默认目的地名称
mq.queue=hello
#topic默认目的地名称
mq.topic=hello
#消息监听者 topic
mq.topic.listener=com.example.activemq.TopicMsgListener
#消息监听者 queue
mq.queue.listener=com.example.activemq.QueueMsgListener
#必须connection上仅仅有一个session相关联。如果存在多个session实例则无效
mq.alwaysSessionAsync=true
#是否支持批量确认消息
mq.optimizeAcknowledge=true
#异步发送消息
mq.useAsyncSend=true
#生产者发送消息的最大字节数,与使用异步参数同时使用
mq.producerWindowSize=1048576
#Session缓存数量
mq.sessionCacheSize=100
#传递的object包白名单,参考http://activemq.apache.org/objectmessage.html
mq.trustedPackages=com.example.pojo
spring.xml
1、spring-mvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.example"/>
<mvc:annotation-driven/>
</beans>
2、spring-activemq-parent.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"
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:property-placeholder location="classpath:config.properties"/>
<!--配置JMS连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${mq.url}"/>
<property name="userName" value="${mq.username}"/>
<property name="password" value="${mq.password}"/>
<property name="alwaysSessionAsync" value="${mq.alwaysSessionAsync}"/>
<property name="optimizeAcknowledge" value="${mq.optimizeAcknowledge}"/>
<property name="useAsyncSend" value="${mq.useAsyncSend}"/>
<property name="producerWindowSize" value="${mq.producerWindowSize}"/>
<property name="trustedPackages" value="${mq.trustedPackages}"/>
</bean>
<!--Spring管理连接工厂-->
<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="connectionFactory"/>
<!--session缓存数量-->
<property name="sessionCacheSize" value="${mq.sessionCacheSize}"/>
</bean>
</beans>
3、spring-activemq-producer.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.xsd">
<!--公共配置-->
<import resource="spring-activemq-parent.xml"/>
<!--Queue 队列消息模板-->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<property name="pubSubDomain" value="false"/>
<property name="sessionTransacted" value="true"/>
<property name="defaultDestinationName" value="${mq.queue}"/>
</bean>
<!--Topic 发布/订阅消息模板-->
<bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
<constructor-arg ref="connectionFactory" />
<property name="pubSubDomain" value="true" />
<property name="sessionTransacted" value="true"/>
<property name="defaultDestinationName" value="${mq.topic}"/>
</bean>
</beans>
4、spring-activemq-consumer.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.xsd">
<!--公共配置-->
<import resource="spring-activemq-parent.xml"/>
<!--Queue 消费者监听容器-->
<bean id="jmsQueueContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<property name="pubSubDomain" value="false"/>
<property name="destinationName" value="${mq.queue}"/>
<property name="messageListener">
<bean class="${mq.queue.listener}"/>
</property>
<property name="sessionTransacted" value="false"/>
</bean>
<!-- Topic 消息监听容器 -->
<bean id="jmsTopicContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<property name="pubSubDomain" value="true"/>
<property name="destinationName" value="${mq.topic}"/>
<property name="messageListener">
<bean class="${mq.topic.listener}"/>
</property>
<!-- 事务控制(开启后会显著影响消费者性能),默认为false,超过重发次数(缺省为6次)后会发送到死信队列,默认为ActiveMQ.DLQ -->
<property name="sessionTransacted" value="false"/>
</bean>
</beans>
QueueMsgListener
package com.example.activemq;
import lombok.extern.log4j.Log4j2;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Log4j2
public class QueueMsgListener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
TextMessage msg = (TextMessage) message;
if (log.isInfoEnabled()){
log.info("Queue消息:{}", msg.getText());
}
} catch (JMSException e) {
log.error("接收JMS Queue消息失败", e);
}
}
}
TopicMsgListener
package com.example.activemq;
import lombok.extern.log4j.Log4j2;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Log4j2
public class TopicMsgListener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String msg = textMessage.getText();
if (log.isInfoEnabled()){
log.info("Topic消息:{}", msg);
}
} catch (JMSException e) {
log.error("接收JMS Topic消息失败", e);
}
}
}
ActiveMQUtil
package com.example.activemq;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Destination;
@Log4j2
@Component
public class ActiveMQUtil {
/**
* Spring JMS Topic 操作模板
*/
private static JmsTemplate jmsTopicTemplate;
/**
* Spring JMS Queue 操作模板
*/
private static JmsTemplate jmsQueueTemplate;
@Autowired
@Qualifier("jmsTopicTemplate")
public void setJmsTopicTemplate(JmsTemplate jmsTopicTemplate) {
ActiveMQUtil.jmsTopicTemplate = jmsTopicTemplate;
}
@Autowired
@Qualifier("jmsQueueTemplate")
public void setJmsQueueTemplate(JmsTemplate jmsQueueTemplate) {
ActiveMQUtil.jmsQueueTemplate = jmsQueueTemplate;
}
public static JmsTemplate getJmsTopicTemplate() {
return jmsTopicTemplate;
}
public static JmsTemplate getJmsQueueTemplate() {
return jmsQueueTemplate;
}
/**
* 发送JMS Topic消息,使用默认目的地
*
* @param message
*/
public static void sendTopicMessage(Object message) {
try {
jmsTopicTemplate.convertAndSend(message);
} catch (Exception e) {
log.error("发送JMS Topic消息失败", e);
}
}
/**
* 发送JMS Topic消息
*
* @param destinationName
* @param message
*/
public static void sendTopicMessage(String destinationName, final Object message) {
try {
jmsTopicTemplate.convertAndSend(destinationName, message);
} catch (Exception e) {
log.error("发送JMS Topic消息失败", e);
}
}
/**
* 发送JMS Topic消息
*
* @param destination
* @param message
*/
public static void sendTopicMessage(Destination destination, final Object message) {
try {
jmsTopicTemplate.convertAndSend(destination, message);
} catch (Exception e) {
log.error("发送JMS Topic消息失败", e);
}
}
/**
* 发送JMS Queue消息,使用默认目的地
*
* @param message
*/
public static void sendQueueMessage(Object message) {
try {
jmsQueueTemplate.convertAndSend(message);
} catch (Exception e) {
log.error("发送JMS Queue消息失败", e);
}
}
/**
* 发送JMS Queue消息
*
* @param destinationName
* @param message
*/
public static void sendQueueMessage(String destinationName, final Object message) {
try {
jmsQueueTemplate.convertAndSend(destinationName, message);
} catch (Exception e) {
log.error("发送JMS Queue消息失败", e);
}
}
/**
* 发送JMS Queue消息
*
* @param destination
* @param message
*/
public static void sendQueueMessage(Destination destination, final Object message) {
try {
jmsQueueTemplate.convertAndSend(destination, message);
} catch (Exception e) {
log.error("发送JMS Queue消息失败", e);
}
}
}
User
用来测试发送和接收Object类型
package com.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
@Getter
@Setter
@AllArgsConstructor
@ToString
public class User implements Serializable {
private static final long serialVersionUID = 4560591454591857339L;
private String name;
private String password;
}
ActiveMQObjectMessage objectMessage = (ActiveMQObjectMessage) message;
User user = (User) objectMessage.getObject();
ReceiveTest
package com.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.IOException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/spring-activemq-consumer.xml"})
public class ReceiveTest {
@Test
public void run() throws IOException {
// 阻塞当前代码
System.in.read();
}
}
SendTest
package com.example;
import com.example.activemq.ActiveMQUtil;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@Log4j2
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/spring-activemq-producer.xml","classpath:spring/spring-mvc.xml"})
public class SendTest {
@Test
public void sendQueue(){
ActiveMQUtil.sendQueueMessage("只有一个能接收到");
}
@Test
public void sendTopic(){
ActiveMQUtil.sendTopicMessage("都能接收到");
}
}
测试
Queue
先跑起来几个ReceiveTest
实例
发送一条消息
其中一个接收到消息并输出
Topic
都接收到了消息