最近使用activeMQ整合spring,用侦听器容器方法写了几个异步消息机制:
一、配置文件spring-activeMq.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:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 1. ************************************配置JMSTemplate************************************************-->
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
<!--设置等待延时时间-->
<property name="receiveTimeout" value="100000"/>
<!-- 消息转换器,发送消息时默认会调用,但要使用convertAndSend()方法 -->
<!--<property name="messageConverter" ref="emailMessageConverter"/>-->
</bean>
<!--事物管理-->
<bean id="transactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<!--启动外部事物,即全局事务-->
<!--
<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"></bean>
-->
<!--消息转换器-->
<bean id="emailMessageConverter" class="spring_activeMQ.messageConveter.EmailMessageConverter"/>
<!-- 2. **************************************定义消息队列***********************************************-->
<!--这个是队列目的地,包含一个队列名称或主题名称的构造参数-->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>queue</value>
</constructor-arg>
</bean>
<!--这个是队列目的地,包含一个队列名称或主题名称的构造参数-->
<bean id="queueTransDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>queueTrans</value>
</constructor-arg>
</bean>
<!--这个是队列目的地,包含一个队列名称或主题名称的构造参数-->
<bean id="sessionQueueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>sessionQueue</value>
</constructor-arg>
</bean>
<!--这个是队列目的地,包含一个队列名称或主题名称的构造参数-->
<bean id="adapterDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>adapterQueue</value>
</constructor-arg>
</bean>
<!-- 3. ****************************************定义生产者跟消费者********************************************-->
<!--注入消息发送者-->
<bean id="producerService" class="spring_activeMQ.producers.ProducerImpl">
<property name="jmsTemplate" ref="jmsTemplate"></property>
</bean>
<!--注入消息发送者-->
<bean id="producerObj" class="spring_activeMQ.producers.ProducerForObject">
<property name="jmsTemplate" ref="jmsTemplate"></property>
</bean>
<!--消费者1: 普通消息侦听器,注入消息侦听器,即接收者-->
<bean id="consumerMessageListener" class="spring_activeMQ.consumers.ConsumerMessageListener">
<!--消息转换器-->
<property name="messageConverter" ref="emailMessageConverter"></property>
</bean>
<!--消费者2: 普通消息侦听器,与上面无异,但能管理事物-->
<bean id="consumerTrascationMessageListener" class="spring_activeMQ.consumers.ComsumerTrascationListener"></bean>
<!--消费者3: SessionAwareMessageListener消息侦听器 -->
<bean id="consumerSessionAwareMessageListener" class="spring_activeMQ.consumers.ConsumerSessionAwareMessageListener">
<!--消息确认目的地属性-->
<property name="destination" ref="queueDestination"></property>
</bean>
<!--消费者4: messageListenerAdapter消息侦听器 -->
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<!--指定处理消息的类,是普通类-->
<property name="delegate">
<bean class="spring_activeMQ.consumers.ConsumerAdapterListener"/>
</property>
<!--指定处理消息的方法,若没有指定,便调用默认处理方法:handleMessage-->
<property name="defaultListenerMethod" value="receiveMessage"/>
<!--指定默认的回复地址,若消息也设置了JMSReplyTo消息头属性,则使用消息头中的地址-->
<property name="defaultResponseDestination" ref="queueDestination"/>
<!--消息转换器,接收消息是默认调用,可以不指定-->
<property name="messageConverter" ref="emailMessageConverter"/>
</bean>
<!-- 4. *************************************以下是利用jms命名空间来定义侦听器容器及侦听器***********************************-->
<jms:listener-container connection-factory="connectionFactory" concurrency="10" transaction-manager="transactionManager">
<!--destination:制定的队列名称; ref:指定的侦听器-->
<jms:listener destination="queue" ref="consumerMessageListener"/>
<jms:listener destination="sessionQueue" ref="consumerSessionAwareMessageListener"/>
<jms:listener destination="adapterQueue" ref="messageListenerAdapter"/>
</jms:listener-container>
<!--*********************此方法也是定义侦听器容器及侦听器,与上面效果一样,但不推荐使用,太过累赘,每一个侦听器都配一个侦听器容器,烦的一塌糊涂************************************-->
<!-- 消息监听容器,容器上绑定了连接工厂、消息目的地、消息侦听器的bean,这里验证事物管理 -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!--连接工厂-->
<property name="connectionFactory" ref="connectionFactory" />
<!--消息目的地-->
<property name="destination" ref="queueTransDestination" />
<!--消息侦听器1-->
<property name="messageListener" ref="consumerTrascationMessageListener" />
<!--并发侦听器线程数-->
<property name="concurrentConsumers" value="10"/>
<!--启动事物-->
<property name="sessionTransacted" value="true"/>
</bean>
<!-- SessionAwareMessageListener的消息监听容器,容器上绑定了连接工厂、消息目的地、消息侦听器的bean -->
<!--<bean id="jmsSessionContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!–连接工厂–>
<property name="connectionFactory" ref="connectionFactory" />
<!–消息目的地–>
<property name="destination" ref="sessionQueueDestination" />
<!–消息侦听器2–>
<property name="messageListener" ref="consumerSessionAwareMessageListener" />
<!–并发侦听器线程数–>
<property name="concurrentConsumers" value="10"/>
</bean>-->
<!-- SessionAwareMessageListener的消息监听容器,容器上绑定了连接工厂、消息目的地、消息侦听器的bean -->
<!-- <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!–连接工厂–>
<property name="connectionFactory" ref="connectionFactory" />
<!–消息目的地–>
<property name="destination" ref="adapterDestination" />
<!–消息侦听器2–>
<property name="messageListener" ref="messageListenerAdapter" />
<!–并发侦听器线程数–>
<property name="concurrentConsumers" value="10"/>
</bean>-->
</beans>
二、配置文件spring-applications.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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<import resource="classpath*:beans-test.xml" />
<import resource="classpath*:spring-activeMq.xml" />
</beans>
三、消息侦听器1
/**
* 消息侦听器:能够进行事务管理的侦听器
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-17
* Time: 下午1:50
* To change this template use File | Settings | File Templates.
*/
public class ComsumerTrascationListener implements MessageListener {
public void onMessage(Message message) {
//这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换
if(message instanceof TextMessage) {
TextMessage textMsg = (TextMessage)message;
System.out.println("接收到一个纯文本消息。");
try {
if (1 != 1) { //这里抛出异常用于检测事物,此条消息会回滚,并在再次启动时再次发送
throw new RuntimeException("Error");
}else{
System.out.println("消息内容是:" + textMsg.getText());
System.out.println("---------------------------------");
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
四、消息侦听器2:
/**
* 消息侦听器:此类本来是普通类,但一旦被MessageListenerAdapter管理,便成为消息侦听器了
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-16
* Time: 下午3:50
* To change this template use File | Settings | File Templates.
*/
public class ConsumerAdapterListener {
public void handleMessage(String message) {
System.out.println("ConsumerListener通过handleMessage接收到一个纯文本消息,消息内容是:" + message);
}
//指定的处理消息方法体,若返回为空,则不回复确认信息,否则回复确认信息(自动回复)
public String receiveMessage(String message) {
System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message);
String replyContent = "ConsumerAdapterListener已收到消息并处理"; //回复内容
return replyContent; //返回时会自动回复
}
public void receiveMessage(Email email) {
System.out.println("接收到一个包含Email的ObjectMessage。");
System.out.println(email);
}
}
五、消息侦听器3:
package spring_activeMQ.consumers;
import org.springframework.jms.support.converter.MessageConverter;
import spring_activeMQ.pojo.Email;
import javax.jms.*;
/**
* 实现MessageListener接口的侦听器,此侦听器单纯接收消息
* 消息侦听器,用于异步接收处理消息
* 实现MessageListener接口
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-16
* Time: 上午10:06
* To change this template use File | Settings | File Templates.
*/
public class ConsumerMessageListener implements MessageListener {
private MessageConverter messageConverter;
public MessageConverter getMessageConverter() {
return messageConverter;
}
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
public void onMessage(Message message) {
//这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换
if(message instanceof TextMessage) {
TextMessage textMsg = (TextMessage)message;
System.out.println("接收到一个纯文本消息。");
try {
System.out.println("消息内容是:" + textMsg.getText());
System.out.println("---------------------------------");
} catch (JMSException e) {
e.printStackTrace();
}
}else if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
try {
// Object obj = objMessage.getObject();
// Email email = (Email) obj; //强制转换,其实也可以使用
Email email = (Email)messageConverter.fromMessage(message);
System.out.println("接收到一个ObjectMessage,包含Email对象。");
System.out.println(email);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
六、消息侦听器4
package spring_activeMQ.consumers;
import org.springframework.jms.listener.SessionAwareMessageListener;
import javax.jms.*;
/**
* 实现SessionAwareMessageListener接口的消息侦听器,此侦听器可以获取session用以回复确认
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-16
* Time: 下午1:40
* To change this template use File | Settings | File Templates.
*/
public class ConsumerSessionAwareMessageListener implements SessionAwareMessageListener<TextMessage> {
//监听队列
private Destination destination;
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void onMessage(TextMessage message, Session session) throws JMSException {
System.out.println("收到一条消息");
System.out.println("消息内容是:" + message.getText());
MessageProducer producer = session.createProducer(destination);
Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener确认回复");
System.out.println("---------------------------------");
producer.send(textMessage);
}
}
七、消息生产者1
package spring_activeMQ.producers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import javax.jms.Destination;
import java.io.Serializable;
/**
* 词生产者会使用到消息转换器:MessageConverter
* MessageConverter会在配置文件里进行配置
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-17
* Time: 上午9:21
* To change this template use File | Settings | File Templates.
*/
public class ProducerForObject {
@Autowired
private JmsTemplate jmsTemplate;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void sendMessage(Destination destination, final Serializable obj) {
//未使用MessageConverter的情况
/*jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
ObjectMessage objMessage = session.createObjectMessage(obj);
return objMessage;
}
});*/
//使用MessageConverter的情况
jmsTemplate.convertAndSend(destination, obj);
}
}
八、消息生产者2
package spring_activeMQ.producers;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import javax.jms.*;
/**
* 定义生产者,调用jmsTemplate发送消息
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-16
* Time: 上午10:05
* To change this template use File | Settings | File Templates.
*/
public class ProducerImpl{
//jms模板
private JmsTemplate jmsTemplate;
public void sendMessage(Destination destination, final String message, final Destination responseDestination) {
System.out.println("---------------生产者发了一个消息:" + message);
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage(message);
if(responseDestination == null){
return textMessage;
}
textMessage.setJMSReplyTo(responseDestination);
return textMessage;
}
});
}
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
}
//测试
<pre name="code" class="java">package spring_activeMQ.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring_activeMQ.pojo.Email;
import spring_activeMQ.producers.ProducerForObject;
import spring_activeMQ.producers.ProducerImpl;
import javax.jms.Destination;
/**
* Created with IntelliJ IDEA.
* User: wxshi
* Date: 15-3-16
* Time: 上午10:13
* To change this template use File | Settings | File Templates.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-activeMq.xml")
public class ProducerConsumerTest {
@Autowired
private ProducerImpl producerImpl;
@Autowired
private ProducerForObject producerObj;
@Qualifier("queueDestination")
@Autowired
private Destination queueDestination;
@Qualifier("queueTransDestination")
@Autowired
private Destination queueTransDestination;
@Qualifier("sessionQueueDestination")
@Autowired
private Destination sessionDestination;
@Qualifier("adapterDestination")
@Autowired
private Destination adapterDestination;
@Test
public void testSend() {
producerImpl.sendMessage(queueTransDestination, "你好,生产者!这是消息",null);
}
@Test //使用到消息转换器
public void testObjectMessage() {
Email email = new Email("zhangsan@xxx.com", "主题", "内容");
producerObj.sendMessage(queueDestination, email);
}
}