一、ActiveMQ入門執行個體
二、Spring整合JMS(一)——基于ActiveMQ實作
三、Spring整合JMS(二)——三種消息監聽器
四、Spring整合JMS(三)——MessageConverter消息轉換器
五、Spring整合JMS(四)——事務管理
消息轉換器MessageConverter
MessageConverter的作用主要有兩方面,一方面它可以把我們的非标準化Message對象轉換成我們的目标Message對象,這主要是用在發送消息的時候;另一方面它又可以把我們的Message對象轉換成對應的目标對象,這主要是用在接收消息的時候。
下面我們就拿發送一個對象消息來舉例,假設我們有這樣一個需求:我們平台有一個發送郵件的功能,進行發送的時候我們隻是把我們的相關資訊封裝成一個JMS消息,然後利用JMS進行發送,在對應的消息監聽器進行接收到的消息處理時才真正的進行消息發送。
假設我們有這麼一個Email對象:
<span style="color:#000000">package cn.tzz.jms.activemq.spring.obj;
import java.io.Serializable;
public class Email implements Serializable {
private static final long serialVersionUID = 1L;
private String receiver;
private String title;
private String content;
public Email(String receiver, String title, String content) {
this.receiver = receiver;
this.title = title;
this.content = content;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Email [receiver=" + receiver + ", title=" + title
+ ", content=" + content + "]";
}
}
</span>
這個Email對象包含了一個簡單的接收者email位址、郵件主題和郵件内容。我們在發送的時候就把這個對象封裝成一個ObjectMessage進行發送,使用MessageConverter的時候我們在使用JmsTemplate進行消息發送時隻需要調用其對應的convertAndSend方法即可。代碼如下所示:
<span style="color:#000000"><span style="color:#000000">import java.io.Serializable;
import javax.jms.Destination;
public interface EntityProducerService {
public void sendMessage(Destination destination, final Serializable obj);
}
</span></span>
<span style="color:#000000"><span style="color:#000000">import java.io.Serializable;
import javax.jms.Destination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import cn.tzz.jms.activemq.spring.obj.service.EntityProducerService;
@Service(value="entityProducerService")
public class EntityProducerServiceImpl implements EntityProducerService {
@Autowired
private 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);
}
}</span></span>
這樣JmsTemplate就會在其内部調用預定的MessageConverter對我們的消息對象進行轉換,然後再進行發送。
這個時候我們就需要定義我們的MessageConverter了。要定義自己的MessageConverter很簡單,隻需要實作Spring為我們提供的MessageConverter接口即可。我們先來看一下Spring MessageConverter接口的定義:
<span style="color:#000000"><span style="color:#000000">public interface MessageConverter {
Message toMessage(Object object, Session session);
Object fromMessage(Message message);
}</span></span>
我們可以看到其中一共定義了兩個方法fromMessage和toMessage,fromMessage是用來把一個JMS Message轉換成對應的Java對象,而toMessage方法是用來把一個Java對象轉換成對應的JMS Message。因為我們已經知道上面要發送的對象就是一個Email對象,是以在這裡我們就簡單地定義一個EmailMessageConverter用來把Email對象和對應的ObjectMessage進行轉換,其代碼如下:
<span style="color:#000000">import java.io.Serializable;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import org.springframework.jms.support.converter.MessageConverter;
public class EmailMessageConverter implements MessageConverter {
public Message toMessage(Object object, Session session) {
ObjectMessage objectMessage = null;
try {
objectMessage = session.createObjectMessage((Serializable) object);
} catch (JMSException e) {
e.printStackTrace();
}
return objectMessage;
}
public Object fromMessage(Message message) {
ObjectMessage objMessage = null;
Serializable serializable = null;
try {
objMessage = (ObjectMessage) message;
serializable = objMessage.getObject();
} catch (JMSException e) {
e.printStackTrace();
}
return serializable;
}
}</span>
這樣當我們利用JmsTemplate的convertAndSend方法發送一個Email對象的時候就會把對應的Email對象當做參數調用我們定義好的EmailMessageConverter的toMessage方法。
定義好我們的EmailMessageConverter之後就需要指定我們用來發送Email對象的JmsTemplate對象的messageConverter為EmailMessageConverter,這裡我們在Spring的配置檔案中定義JmsTemplate bean的時候就指定:
<span style="color:#000000"><!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->
<property name="connectionFactory" ref="connectionFactory" />
<!-- 消息轉換器 -->
<property name="messageConverter" ref="emailMessageConverter"/>
</bean>
<!-- 類型轉換器 -->
<bean id="emailMessageConverter" class="cn.tzz.jms.activemq.spring.obj.service.impl.EmailMessageConverter"/> </span>
到此我們的MessageConverter就定義好了,也能夠進行使用了.
上面destination對應的接收處理的MessageListener方法如下所示:
<span style="color:#000000">public class ConsumerObjMessageListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
try {
Object obj = objMessage.getObject();
Email email = (Email) obj;
System.out.println("接收到一個ObjectMessage,包含Email對象。");
System.out.println(email);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
</span>
之前說了MessageConverter有兩方面的功能,除了把Java對象轉換成對應的Jms Message之外還可以把Jms Message轉換成對應的Java對象。我們看上面的消息監聽器在接收消息的時候接收到的就是一個Jms Message,如果我們要利用MessageConverter來把它轉換成對應的Java對象的話,隻能是我們往裡面注入一個對應的MessageConverter,然後在裡面手動的調用,如:
<span style="color:#000000"><span style="color:#000000">package cn.tzz.jms.activemq.spring.listener;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import org.springframework.jms.support.converter.MessageConverter;
import cn.tzz.jms.activemq.spring.obj.Email;
public class ConsumerObjMessageListener implements MessageListener {
private MessageConverter emailMessageConverter;
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
try {
// Object obj = objMessage.getObject();
// Email email = (Email) obj;
Email email = (Email) emailMessageConverter.fromMessage(objMessage);
System.out.println("ConsumerObjMessageListener");
System.out.println("接收到一個ObjectMessage,包含Email對象。");
System.out.println(email);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public MessageConverter getEmailMessageConverter() {
return emailMessageConverter;
}
public void setEmailMessageConverter(MessageConverter emailMessageConverter) {
this.emailMessageConverter = emailMessageConverter;
}
}</span></span>
當我們使用MessageListenerAdapter來作為消息監聽器的時候,我們可以為它指定一個對應的MessageConverter,這樣Spring在處理接收到的消息的時候就會自動地利用我們指定的MessageConverter對它進行轉換,然後把轉換後的Java對象作為參數調用指定的消息處理方法。這裡我們再把前面講解MessageListenerAdapter時定義的MessageListenerAdapter拿來做一個測試,我們指定它的MessageConverter為我們定義好的EmailMessageConverter。
<span style="color:#000000"><span style="color:#000000"> <context:annotation-config />
<context:component-scan base-package="cn.tzz.jms.activemq.spring.obj" />
<!-- ActiveMQ -->
<!-- 真正可以産生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory" ref="targetConnectionFactory"/>
<property name="maxConnections" value="10"/>
</bean>
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="pooledConnectionFactory"/>
</bean>
<!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->
<property name="connectionFactory" ref="connectionFactory" />
<!-- 消息轉換器 -->
<property name="messageConverter" ref="emailMessageConverter"/>
</bean>
<!--消息隊列的目的地,點對點的 -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>queue</value>
</constructor-arg>
</bean>
<!--這個是主題目的地,一對多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="topic" />
</bean>
<bean id="adapterQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>adapterQueue</value>
</constructor-arg>
</bean>
<!-- 類型轉換器 -->
<bean id="emailMessageConverter" class="cn.tzz.jms.activemq.spring.obj.service.impl.EmailMessageConverter"/>
<!-- 自定義消息監聽 -->
<bean id="consumerObjMessageListener" class="cn.tzz.jms.activemq.spring.listener.ConsumerObjMessageListener">
<property name="emailMessageConverter" ref="emailMessageConverter"/>
</bean>
<!-- MessageListenerAdapter 消息監聽 -->
<bean id="objMessageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate">
<bean class="cn.tzz.jms.activemq.spring.listener.ConsumerListener"/>
</property>
<property name="defaultListenerMethod" value="receiveMessage"/>
<property name="messageConverter" ref="emailMessageConverter"/>
</bean>
<!-- 消息監聽擴充卡對應的監聽容器 -->
<bean id="objMessageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="adapterQueue"/>
<!-- <property name="messageListener" ref="objMessageListenerAdapter"/> -->
<property name="messageListener" ref="consumerObjMessageListener"/>
</bean></span></span>
然後在我們的真正用于處理接收到的消息的ConsumerListener中添加一個receiveMessage方法,添加後其代碼如下所示:
<span style="color:#000000"><span style="color:#000000">public class ConsumerListener {
public void handleMessage(String message) {
System.out.println("ConsumerListener通過handleMessage接收到消息:" + message);
}
// public void receiveMessage(String message) {
// System.out.println("ConsumerListener通過receiveMessage接收到消息:" + message);
// }
// public String receiveMessage(String message) {
// System.out.println("ConsumerListener通過receiveMessage接收到消息:" + message);
// return "這是ConsumerListener對象的receiveMessage方法的傳回值。";
// }
public void receiveMessage(Email email) {
System.out.println("接收到一個包含Email的ObjectMessage。");
System.out.println(email);
}
}</span></span>
然後我們定義如下測試代碼:
<span style="color:#000000"><span style="color:#000000">@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext2.xml")
public class EntiryProducerConsumerTest {
@Resource(name="entityProducerService")
private EntityProducerService entityProducerService;
@Autowired
@Qualifier("adapterQueue")
private Destination adapterQueue;
@Test
public void testObjectMessage() {
Email email = new Email("[email protected]", "測試", "HellWorld");
entityProducerService.sendMessage(adapterQueue, email);
}
}</span></span>
因為我們給MessageListenerAdapter指定了一個MessageConverter,而且是一個EmailMessageConverter,是以當MessageListenerAdapter接收到一個消息後,它會調用我們指定的MessageConverter的fromMessage方法把它轉換成一個Java對象,根據定義這裡會轉換成一個Email對象,然後會把這個Email對象作為參數調用我們通過defaultListenerMethod屬性指定的預設處理器方法,根據定義這裡就是receiveMessage方法,但是我們可以看到在ConsumerListener中我們一共定義了兩個receiveMessage方法,因為是通過轉換後的Email對象作為參數進行方法調用的,是以這裡調用的就應該是參數類型為Email的receiveMessage方法了。
上述測試代碼運作後會輸出如下結果:
接收到一個ObjectMessage,包含Email對象。
Email [[email protected], title=測試, content=HellWorld]
說到這裡可能有讀者就會有疑問了,說我們在之前講解MessageListenerAdapter的時候不是沒有指定對應的MessageConverter,然後發送了一個TextMessage,結果Spring還是把它轉換成一個String對象,調用了ConsumerListener參數類型為String的receiveMessage方法嗎?那你這個MessageConverter在MessageListenerAdapter進行消息接收的時候也沒什麼用啊。
其實還是有用的,在我們使用MessageListenerAdapter時,在對其進行初始化也就是調用其構造方法時,它會預設new一個Spring已經為我們實作了的MessageConverter——SimpleMessageConverter作為其預設的MessageConverter,這也就是為什麼我們在使用MessageListenerAdapter的時候不需要指定MessageConverter但是消息還是會轉換成對應的Java對象的原因。是以預設情況下我們使用MessageListenerAdapter時其對應的MessageListener的處理器方法參數類型必須是一個普通Java對象,而不能是對應的Jms Message對象。
那如果我們在處理Jms Message的時候想使用MessageListenerAdapter,然後又希望處理最原始的Message,而不是經過MessageConverter進行轉換後的Message該怎麼辦呢?這個時候我們隻需要在定義MessageListenerAdapter的時候指定其MessageConverter為空就可以了。
<span style="color:#000000"> <!-- 消息監聽擴充卡 -->
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate">
<bean class="com.tiantian.springintejms.listener.ConsumerListener"/>
</property>
<property name="defaultListenerMethod" value="receiveMessage"/>
<property name="messageConverter">
<null/>
</property>
</bean>
</span>
那麼這個時候我們的真實MessageListener的處理器方法參數類型就應該是Jms Message或對應的Jms Message子類型了,不然就會調用不到對應的處理方法了。這裡因為我們發送的是一個ObjectMessage,是以這裡就添加一個對應的參數類型為ObjectMessage的receiveMessage方法了。
<span style="color:#000000"> public void receiveMessage(ObjectMessage message) throws JMSException {
System.out.println(message.getObject());
}
</span>
剛剛講到Spring已經為我們實作了一個簡單的MessageConverter,即org.springframework.jms.support.converter.SimpleMessageConverter,其實Spring在初始化JmsTemplate的時候也指定了其對應的MessageConverter為一個SimpleMessageConverter,是以如果我們平常沒有什麼特殊要求的時候可以直接使用JmsTemplate的convertAndSend系列方法進行消息發送,而不必繁瑣的在調用send方法時自己new一個MessageCreator進行相應Message的建立。