原文地址:http://blog.csdn.net/haoxingfeng/article/details/9167895
在我们和Spring 整合的时候,对于消费者而言我们有三种不同类型的监听器可以去选择,他们分别是MessageListener,SessionAwareMessageListener,MessageListenerAdapter,下面我们就来说说他们之间的区别。
MessageListener是最原始的消息监听器,是JMS规范中定义的接口。其中定义了用于处理消息的onMessage(Message message) 接口,改方法只接受一个Message类型的参数,在JMS--ActiveMq与spring整合(一)种我们使用的就是MessageListener。
SessionAwareMessageListener 并不是原始的消息监听器。它是Spring为我们提供的,它不是标准的JMS MessageListener。原始的MessageListener只是纯粹的用来接收消息,假如我们在使用MessageListener处理接收到的消息时我们需要发送一个消息通知对方我们已经收到这个消息了,那么这个时候我们就需要在代码里面去重新获取一个Connection或Session。SessionAwareMessageListener的设计就是为了方便我们在接收到消息后发送一个回复的消息,它同样为我们提供了一个处理接收到的消息的onMessage方法,但是这个方法可以同时接收两个参数,一个是表示当前接收到的消息Message,另一个就是可以用来发送消息的Session对象。
xml配置:
<bean id="sessionAwareQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>sessionAwareQueue</value>
</constructor-arg>
</bean>
<bean id="sessionAwareConsumerMessageListener" class="com.ghq.activemq.SessionAwareConsumer">
<property name="destination" ref="queue"></property>
</bean>
<bean id="sessionAwareMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="destination" ref="sessionAwareQueue"></property>
<property name="messageListener" ref="sessionAwareConsumerMessageListener"></property>
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
Java代码:
public class SessionAwareConsumer implements SessionAwareMessageListener<Message> {
private Destination destination;
public void onMessage(Message message, Session session) throws JMSException {
TextMessage tMessage = (TextMessage) message;
System.out.println("SessionAwareConsumer接收消息:"+tMessage.getText());
MessageProducer producer = session.createProducer(destination);
producer.send(session.createTextMessage(tMessage.getText()));
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
}
producer发送了一个消息到sessionAwareQueue ,sessionAwareMessageListenerContainer接收消息将消息传递给真正的处理这SessionAwareConsumer,SessionAwareConsumer出来了消息并向queue 输出了改消息,并向queue发送了改消息。messageListenerContainer 处理了改消息。
MessageListenerAdapter实现了SessionAwareMessageListener和MessageListener接口。它的主要作用是将接收到的消息进行类型转换
然后通过反射的形式把它交给一个普通的Java类进行处理。
MessageListenerAdapter会把接收到的消息做如下转换:
TextMessage转换为String对象;
BytesMessage转换为byte数组;
MapMessage转换为Map对象;
ObjectMessage转换为对应的Serializable对象。
既然前面说了MessageListenerAdapter会把接收到的消息做一个类型转换,然后利用反射把它交给真正的目标处理器——一个普通的Java类进行处理(如果真正的目标处理器是一个MessageListener或者是一个SessionAwareMessageListener,那么Spring将直接使用接收到的Message对象作为参数调用它们的onMessage方法,而不会再利用反射去进行调用),那么我们在定义一个MessageListenerAdapter的时候就需要为它指定这样一个目标类。这个目标类我们可以通过MessageListenerAdapter的构造方法参数指定,如:
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg><bean class="com.ghq.activemq.Consumer"/></constructor-arg>
</bean>
也可以通过它的delegate属性来指定,如:
xml:
<bean id="adapterQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>adapterQueue</value>
</constructor-arg>
</bean>
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate">
<bean class="com.ghq.activemq.AdapterRealClass"></bean>
</property>
<!-- 默认调用 com.ghq.activemq.AdapterRealClass 类的handleMessage方法,也可以通过defaultListenerMethod 属性来设置方法名字-->
<!-- <property name="defaultListenerMethod" value="myhandleMessage"></property> -->
</bean>
<bean id="adapterMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="destination" ref="adapterQueue"></property>
<property name="messageListener" ref="messageListenerAdapter"></property>
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
MessageListenerAdapter除了会自动的把一个普通Java类当做MessageListener来处理接收到的消息之外,其另外一个主要的功能是可以自动的发送返回消息。
当我们用于处理接收到的消息的方法的返回值不为空的时候,Spring会自动将它封装为一个JMS Message,然后自动进行回复。那么这个时候这个回复消息将发送到哪里呢?这主要有两种方式可以指定。
第一,可以通过发送的Message的setJMSReplyTo方法指定该消息对应的回复消息的目的地。这里我们把我们的生产者发送消息的代码做一下修改,在发送消息之前先指定该消息对应的回复目的地为一个叫responseQueue的队列目的地,具体代码如下所示:
在发送这代码中加入下面一行话:
TextMessage tMessage = session.createTextMessage(message);
tMessage.setJMSReplyTo(responseQueue);
xml配置(去掉上面调用默认方法handleMessage的注释,调用我们自己的myhandleMessage):
<bean id="responseQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>responseQueue</value>
</constructor-arg>
</bean>
<bean id="responseQueueListener" class="com.ghq.activemq.ResponseQueueListener" />
<bean id="adapterMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="destination" ref="responseQueue"></property>
<property name="messageListener" ref="responseQueueListener"></property>
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
简单java类:
public class AdapterRealClass {
public void handleMessage(String message){
System.out.println("AdapterRealClass handleMessage():"+message);
}
public String myhandleMessage(String message){
System.out.println("AdapterRealClass myhandleMessage():"+message);
return "myhandleMessage"+message;
}
}
第二,通过MessageListenerAdapter的defaultResponseDestination属性来指定。这里我们也来做一个测试,首先维持生产者发送消息的代码不变,即发送消息前不通过Message的setJMSReplyTo方法指定消息的回复目的地。
既然我们可以通过两种方式来指定MessageListenerAdapter自动发送回复消息的目的地,那么当我们两种方式都指定了而且它们的目的地还不一样的时候会怎么发送呢?当两种方式都指定了消息的回复目的地的时候使用发送消息的setJMSReplyTo方法指定的目的地将具有较高的优先级,MessageListenerAdapter将只往该方法指定的消息回复目的地发送回复消息。