jms點對點和訂閱發佈模式的詳解

《一》

1.ActiveMQ概述

    企業消息軟件從80年代起就存在,它不只是一種應用間消息傳遞風格,也是一種集成風格。因此,消息傳遞可以滿足應用間的通知和互相操作。但是開源的解決方案是到最近10年纔出現的。Apache ActiveMQ就是其中一種。它使應用間能以異步,松耦合方式交流。ActiveMQ 是Apache出品,最流行的,能力強勁的開源消息總線。

 ?   ActiveMQ是Apache軟件基金下的一個開源軟件,它遵循JMS規範(Java Message Service),是消息驅動中間件軟件(MOM)。它爲企業消息傳遞提供高可用,出色性能,可擴展,穩定和安全保障。ActiveMQ使用Apache許可協議。因此,任何人都可以使用和修改它而不必反饋任何改變。這對於商業上將ActiveMQ用在重要用途的人尤爲關鍵。MOM的工作是在分佈式的各應用之間調度事件和消息,使之到達指定的接收者。所以高可用,高性能,高可擴展性尤爲關鍵。

2.ActiveMQ特性

 

    ⒈支持多種語言客戶端,如:Java,C,C++,C#,Ruby,Perl,Python,PHP。應用協議有 OpenWire,Stomp REST,WS Notification,XMPP,AMQP。

    ⒉ 完全支持JMS1.1和J2EE1.4規範,它們包括同步和異步消息傳遞,一次和只有一次的消息傳遞,對於預訂者的持久消息等等。依附於JMS規範意味着,不論JMS消息提供者是誰,同樣的基本特性(持久化,XA消息,事務)都是有效的。

    ⒊ 對spring的支持,ActiveMQ可以很容易內嵌到使用Spring的系統裏面去。

    ⒋ 通過了常見J2EE服務器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的測試,其中通過JCA 1.5 resource adaptors的配置,可以讓ActiveMQ可以自動的部署到任何兼容J2EE 1.4 商業服務器上。

    ⒌ ActiveMQ提供各種連接選擇,包括HTTP,HTTPS,IP多點傳送,SSL,STOMP,TCP,UDP,XMPP等。大量的連接協議支持使之具有更好的靈活性。很多現有的系統使用一種特定協議並且不能改變,所以一個支持多種協議的消息平臺降低了使用的門檻。雖然連接很重要,但是和其他容器集成也同樣重要。

    6.ActiveMQ提供多種持久性方案可供選擇,也可以完全按自己需求定製驗證和授權。例如,ActiveMQ通過KahaDB提供自己的超快速消息持久方案(ultra-fast message persistence),但也支持標準的JDBC方案。ActiveMQ可以通過配置文件提供簡單的驗證和授權,也提供標準的JAAS登陸模塊。

    7.ActiveMQ是爲開發者設計的。它並不需要專門的管理工具,因爲它提供各種易用且強大的管理特性。有很多方法去監控ActiveMQ的各個方面,可以通過JMX使用JConsole或ActiveMQ web console;可以運行ActiveMQ消息報告;可以用命令行腳本;可以通過日誌。

    8.代理器集羣(Broker clustering)----爲了利於擴展,多個ActiveMQ broker能夠聯合工作。這個方式就是network of brokers並且能支持多種拓撲結構;支持客戶端-服務器,點對點。

    9.支持Ajax, 支持與Axis的整合

 

3.ActiveMQ優勢

    1.與OpenJMS、JbossMQ等開源jms provider相比,ActiveMQ有Apache的支持,持續發展的優勢明顯。

    2.速度很快,JBossMQ的十倍(沒有測試)

 

    3.提高系統資源的利用率,主要是任務的派發不是24小時平均的,而是高峯時期任務量很多,比如1秒1000多個,有的時候很低,比如十幾秒鐘纔來一個。應用服務通過JMS隊列一個一個的取任務,做完一個再領一個,使系統資源的運用趨於平均。而JMS,比如JMS接收消息的效率是很高的,比如ActiveMQ,在賽揚(2.40GHz)機器上能夠達到2000/s,消息大小爲1-2k。好一些的服務器可以達到2萬以上/秒。

 

4.ActiveMQ安裝

    首先去http://activemq.apache.org/download.html 下載最新版本ActiveMQ 5.8.0 Release, 解壓apache-activemq-5.8.0-bin.zip(或者apache-activemq-5.8.0-bin.tar.gz)

目錄如下:

   +bin (windows下面的bat和unix/Linux下面的sh)

   +conf (activeMQ配置目錄,包含最基本的activeMQ配置文件)

   +data (默認是空的)

   +docs (只有index.html)

   +example (幾個例子)

   +lib (activemMQ使用到的lib)

   +webapps(後臺管理頁面)

+webapps-demo(後臺管理消息發送頁面)

   +activemq-all-5.8.0.jar (java開發的jar包)

   -LICENSE.txt

   -NOTICE.txt

   -README.txt

   -user-guide.html

你可以使用bin\activemq.bat(activemq)啓動

啓動ActiveMQ以後,登陸:http://localhost:8161/admin/ 默認用戶/密碼爲admin/admin。

注意:

⒈ 這個僅僅是最基礎的ActiveMQ的配置,很多地方都沒有配置因此不要直接使用這個配置用於生存環境。

⒉ 有的時候由於端口被佔用,導致ActiveMQ錯誤,ActiveMQ可能需要以下端口1099(JMX),61616(默認的TransportConnector)。

⒊ 如果沒有物理網卡,或者MS的LoopBackAdpater Multicast會報一個錯誤

 

5.運行附帶的示例程序 

   1、Queue消息示例: 

     * 啓動Queue消息消費者 

       cd example 

 

  ant consumer 

     * 啓動Queue消息生產者 

       cd example 

       ant producer 
     簡要說明:生產者(producer)發消息,消費者(consumer)接消息,發送/接收2000個消息後自動關閉 

   2、Topic消息示例: 

     * 啓動Topic消息消費者 

       cd example 

       ant topic-listener 

     * 啓動Topic消息生產者 

       cd example 

       ant topic-publisher 
     簡要說明:重複10輪,publisher每輪發送2000個消息,並等待獲取listener的處理結果報告,然後進入下一輪      發送,最後

統計全局發送時間。 

   3、Queue消息和Topic消息發送之後,可以通過後臺點擊Queues和Topics查看發送消息具體信息。

 

6.ActiveMQ類別及開發流程

   1)、Point-to-Point (點對點)消息模式開發流程 :
       1、生產者(producer)開發流程(ProducerTool.java): 

         1.1 創建Connection: 根據url,user和password創建一個jms Connection。 

         1.2 創建Session: 在connection的基礎上創建一個session,同時設置是否支持事務和ACKNOWLEDGE標識。 

         1.3 創建Destination對象: 需指定其對應的主題(subject)名稱,producer和consumer將根據subject來發送/接收對應的消息。 

         1.4 創建MessageProducer: 根據Destination創建MessageProducer對象,同時設置其持久模式。 

         1.5 發送消息到隊列(Queue): 封裝TextMessage消息,使用MessageProducer的send方法將消息發送出去。 

       2、消費者(consumer)開發流程(ConsumerTool.java): 

         2.1 實現MessageListener接口: 消費者類必須實現MessageListener接口,然後在onMessage()方法中監聽消息的到達並處理。 

         2.2 創建Connection: 根據url,user和password創建一個jms Connection,如果是durable模式,還需要給connection設置一個clientId。 

         2.3 創建Session和Destination: 與ProducerTool.java中的流程類似,不再贅述。 

         2.4 創建replyProducer【可選】:可以用來將消息處理結果發送給producer。 

         2.5 創建MessageConsumer:  根據Destination創建MessageConsumer對象。 

                          2.6 消費message:  在onMessage()方法中接收producer發送過來的消息進行處理,並可以通過replyProducer反饋信息給producer 

 

[java] view plaincopy
 
  1. if (message.getJMSReplyTo() != null) {    
  2.   replyProducer.send(message.getJMSReplyTo(),     
  3.  session.createTextMessage("Reply: " + message.getJMSMessageID()));  

 

 

   2)、Publisher/Subscriber(發佈/訂閱者)消息模式開發流程 

 

       1、訂閱者(Subscriber)開發流程(TopicListener.java): 

         1.1 實現MessageListener接口: 在onMessage()方法中監聽發佈者發出的消息隊列,並做相應處理。 

         1.2 創建Connection: 根據url,user和password創建一個jms Connection。 

         1.3 創建Session: 在connection的基礎上創建一個session,同時設置是否支持事務和ACKNOWLEDGE標識。 

         1.4 創建Topic:  創建2個Topic, topictest.messages用於接收發布者發出的消息,topictest.control 用於向發佈者發送消息,實現雙方的交互。 

         1.5 創建consumer和producer對象:根據topictest.messages創建consumer,根據topictest.control創建 producer。 

         1.6 接收處理消息:在onMessage()方法中,對收到的消息進行處理,可直接簡單在本地顯示消息,或者根 據消息內容不同處理對應的業務邏輯(比如:數據庫更新、文件操作等等),並且可以使用producer對象將處理結果返回給發佈者。 

       2、發佈者(Publisher)開發流程(TopicPublisher.java):

         2.1 實現MessageListener接口:在onMessage()方法中接收訂閱者的反饋消息。 

         2.2 創建Connection: 根據url,user和password創建一個jms Connection。 

                           2.3 創建Session: 在connection的基礎上創建一個session,同時設置是否支持事務和ACKNOWLEDGE標識。 

         2.4 創建Topic: 創建2個Topic,topictest.messages用於向訂閱者發佈消息,topictest.control用於接 收訂閱者反饋的消息。這2個topic與訂閱者開發流程中的topic是一一對應的。 

         2.5 創建consumer和producer對象: 根據topictest.messages創建publisher; 根據topictest.control 創建consumer,同時監聽訂閱者反饋的消息。

         2.6 給所有訂閱者發送消息,並接收反饋消息:  示例代碼中,一共重複10輪操作。 每輪先向所有訂閱者 發送2000個消息; 然後堵塞線程,開始等待; 最後通過onMessage()方法,接收到訂閱者反饋的“REPORT”類信息後,才print反饋信息並解除線程堵塞,進入下一輪。 
             注:可同時運行多個訂閱者測試查看此模式效果 

7.Eclipse代碼開發

   1.建立一個Web Probject 項目,將activemq-all-5.8.0.jar放在lib裏面

 

   2.Queue(點對點)方式:生產者

[java] view plaincopy
 
  1. package jms;  
  2.   
  3. import javax.jms.Connection;  
  4. import javax.jms.ConnectionFactory;  
  5. import javax.jms.DeliveryMode;  
  6. import javax.jms.Destination;  
  7. import javax.jms.MessageProducer;  
  8. import javax.jms.Queue;  
  9. import javax.jms.Session;  
  10. import javax.jms.TextMessage;  
  11.   
  12. import org.apache.activemq.ActiveMQConnection;  
  13. import org.apache.activemq.ActiveMQConnectionFactory;  
  14.   
  15. //Queue(點對點)方式  生存者Producer  
  16. public class QueueProducer {  
  17.     private static String user = ActiveMQConnection.DEFAULT_USER;  
  18.     private static String password =ActiveMQConnection.DEFAULT_PASSWORD;  
  19.     private static String url =  "tcp://localhost:61616";  
  20.   
  21.     public static void main(String[] args)throws Exception {  
  22.          // ConnectionFactory :連接工廠,JMS 用它創建連接  
  23.         ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user,password,url);  
  24.         // Connection :JMS 客戶端到JMS Provider 的連接  
  25.         Connection connection = connectionFactory.createConnection();  
  26.         // Connection 啓動  
  27.         connection.start();  
  28.         System.out.println("Connection is start...");  
  29.         // Session: 一個發送或接收消息的線程  
  30.         Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);  
  31.         // Queue :消息的目的地;消息發送給誰.  
  32.         Queue  destination = session.createQueue("example.A");  
  33.         // MessageProducer:消息發送者  
  34.         MessageProducer producer = session.createProducer(destination);  
  35.         // 設置不持久化,此處學習,實際根據項目決定  
  36.         producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
  37.          // 構造消息,此處寫死,項目就是參數,或者方法獲取  
  38.         sendMessage(session, producer);  
  39.         session.commit();  
  40.   
  41.         connection.close();  
  42.         System.out.println("send text ok.");  
  43.     }  
  44.       
  45.     public static void sendMessage(Session session, MessageProducer producer)  
  46.             throws Exception {  
  47.         for (int i = 1; i <= 100; i++) {//有限制,達到1000就不行  
  48.             TextMessage message = session.createTextMessage("ActiveMq 發送的消息" + i);  
  49.             // 發送消息到目的地方  
  50.             System.out.println("發送消息:" + "ActiveMq 發送的消息" + i);  
  51.             producer.send(message);  
  52.         }  
  53.     }  
  54. }  

  3.Queue(點對點)方式:消費者

 

[java] view plaincopy
 
  1. package jms;  
  2.   
  3. import javax.jms.Connection;  
  4. import javax.jms.ConnectionFactory;  
  5. import javax.jms.Destination;  
  6. import javax.jms.JMSException;  
  7. import javax.jms.Message;  
  8. import javax.jms.MessageConsumer;  
  9. import javax.jms.MessageListener;  
  10. import javax.jms.Queue;  
  11. import javax.jms.Session;  
  12. import javax.jms.TextMessage;  
  13.   
  14. import org.apache.activemq.ActiveMQConnection;  
  15. import org.apache.activemq.ActiveMQConnectionFactory;  
  16.   
  17. //Queue(點對點)方式  消費這Consumer  
  18. public class QueueConsumer {  
  19.     private static String user = ActiveMQConnection.DEFAULT_USER;  
  20.     private static String password =ActiveMQConnection.DEFAULT_PASSWORD;  
  21.     private static String url = "tcp://localhost:61616";  
  22.     public static void main(String[] args) throws Exception{  
  23.         // ConnectionFactory :連接工廠,JMS 用它創建連接  
  24.         ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user,password,url);  
  25.         // Connection :JMS 客戶端到JMS Provider 的連接  
  26.         Connection connection = connectionFactory.createConnection();  
  27.         connection.start();  
  28.         // Session: 一個發送或接收消息的線程  
  29.         final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);  
  30.         // Destination :消息的目的地;消息發送給誰.  
  31.         Queue destination=session.createQueue("example.A");  
  32.         // 消費者,消息接收者  
  33.         MessageConsumer consumer = session.createConsumer(destination);  
  34.         consumer.setMessageListener(new MessageListener(){//有事務限制  
  35.             @Override  
  36.             public void onMessage(Message message) {  
  37.                 try {  
  38.                     TextMessage textMessage=(TextMessage)message;  
  39.                     System.out.println(textMessage.getText());  
  40.                 } catch (JMSException e1) {  
  41.                     e1.printStackTrace();  
  42.                 }  
  43.                 try {  
  44.                     session.commit();  
  45.                 } catch (JMSException e) {  
  46.                     e.printStackTrace();  
  47.                 }  
  48.             }  
  49.         });  
  50.           
  51. /*  另外一種接受方式 
  52.  *    while (true) { 
  53.               //設置接收者接收消息的時間,爲了便於測試,這裏誰定爲100s 
  54.               TextMessage message = (TextMessage) consumer.receive(100000); 
  55.               if (null != message) { 
  56.                   System.out.println("收到消息" + message.getText()); 
  57.               } else { 
  58.                   break; 
  59.               } 
  60.           }*/  
  61.     }  
  62. }  

 

 

 

 4.Topic(發佈/訂閱)方式:發佈者

 

[java] view plaincopy
 
  1. package jms;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.jms.Connection;  
  6. import javax.jms.ConnectionFactory;  
  7. import javax.jms.DeliveryMode;  
  8. import javax.jms.JMSException;  
  9. import javax.jms.MapMessage;  
  10. import javax.jms.MessageProducer;  
  11. import javax.jms.Queue;  
  12. import javax.jms.Session;  
  13. import javax.jms.TextMessage;  
  14. import javax.jms.Topic;  
  15.   
  16. import org.apache.activemq.ActiveMQConnection;  
  17. import org.apache.activemq.ActiveMQConnectionFactory;  
  18.   
  19. //Topic(發佈/訂閱)方式  發佈者Publisher  
  20. public class TopicPublisher {  
  21.     private static String user = ActiveMQConnection.DEFAULT_USER;  
  22.     private static String password =ActiveMQConnection.DEFAULT_PASSWORD;  
  23.     private static String url =  "tcp://localhost:61616";  
  24.   
  25.     public static void main(String[] args)throws Exception {  
  26.          // ConnectionFactory :連接工廠,JMS 用它創建連接  
  27.         ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user,password,url);  
  28.         // Connection :JMS 客戶端到JMS Provider 的連接  
  29.         Connection connection = connectionFactory.createConnection();  
  30.         // Connection 啓動  
  31.         connection.start();  
  32.         System.out.println("Connection is start...");  
  33.         // Session: 一個發送或接收消息的線程  
  34.         Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);  
  35.         // Topicr :消息的目的地;消息發送給誰.  
  36.         Topic  destination = session.createTopic("example.A");  
  37.         // MessageProducer:消息發送者  
  38.         MessageProducer producer = session.createProducer(destination);  
  39.         // 設置不持久化,此處學習,實際根據項目決定  
  40.         producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
  41.          // 構造消息,此處寫死,項目就是參數,或者方法獲取  
  42.         sendMessage(session, producer);  
  43.         session.commit();  
  44.   
  45.         connection.close();  
  46.         System.out.println("send text ok.");  
  47.     }  
  48.       
  49.     public static void sendMessage(Session session, MessageProducer producer)  
  50.             throws Exception {  
  51.         for (int i = 1; i <= 100; i++) {//有限制,達到1000就不行  
  52.             TextMessage message = session.createTextMessage("ActiveMq 發送的消息" + i);  
  53.             // 發送消息到目的地方  
  54.             System.out.println("發送消息:" + "ActiveMq 發送的消息" + i);  
  55.             producer.send(message);  
  56.         }  
  57.     }  
  58.   
  59. }  



 

 

 

 5.Topic(發佈/訂閱)方式:訂閱者

 

 

[java] view plaincopy
 
  1. package jms;  
  2.   
  3. import javax.jms.Connection;  
  4. import javax.jms.ConnectionFactory;  
  5. import javax.jms.JMSException;  
  6. import javax.jms.Message;  
  7. import javax.jms.MessageConsumer;  
  8. import javax.jms.MessageListener;  
  9. import javax.jms.Session;  
  10. import javax.jms.TextMessage;  
  11. import javax.jms.Topic;  
  12.   
  13. import org.apache.activemq.ActiveMQConnection;  
  14. import org.apache.activemq.ActiveMQConnectionFactory;  
  15.   
  16. //Topic(發佈/訂閱)方式  訂閱者TopicSubscriber  
  17. public class TopicSubscriber {  
  18.     private static String user = ActiveMQConnection.DEFAULT_USER;  
  19.     private static String password =ActiveMQConnection.DEFAULT_PASSWORD;  
  20.     private static String url = "tcp://localhost:61616";  
  21.     public static void main(String[] args) throws Exception{  
  22.         // ConnectionFactory :連接工廠,JMS 用它創建連接  
  23.         ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user,password,url);  
  24.         // Connection :JMS 客戶端到JMS Provider 的連接  
  25.         Connection connection = connectionFactory.createConnection();  
  26.         connection.start();  
  27.         // Session: 一個發送或接收消息的線程  
  28.         final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);  
  29.         // Destination :消息的目的地;消息發送給誰.  
  30.         Topic destination=session.createTopic("example.A");  
  31.         // 消費者,消息接收者  
  32.         MessageConsumer consumer = session.createConsumer(destination);  
  33.         consumer.setMessageListener(new MessageListener(){//有事務限制  
  34.             @Override  
  35.             public void onMessage(Message message) {  
  36.                 try {  
  37.                     TextMessage textMessage=(TextMessage)message;  
  38.                     System.out.println(textMessage.getText());  
  39.                 } catch (JMSException e1) {  
  40.                     e1.printStackTrace();  
  41.                 }  
  42.                 try {  
  43.                     session.commit();  
  44.                 } catch (JMSException e) {  
  45.                     e.printStackTrace();  
  46.                 }  
  47.             }  
  48.         });  
  49.           
  50. /*  另外一種接受方式 
  51.  *    while (true) { 
  52.               //設置接收者接收消息的時間,爲了便於測試,這裏誰定爲100s 
  53.               TextMessage message = (TextMessage) consumer.receive(100000); 
  54.               if (null != message) { 
  55.                   System.out.println("收到消息" + message.getText()); 
  56.               } else { 
  57.                   break; 
  58.               } 
  59.           }*/  
  60.     }  
  61. }  

 

 

 

Queue(點對點)方式和Topic(發佈/訂閱)方式 的運行結果最明顯的區別是:Queue(點對點)方式中先運行生產者,再運行消費者,消費者還能接受到消息;

Topic(發佈/訂閱)方式就不同了,先運行發佈者,再運行訂閱者,訂閱者收到的消息

可能沒有或者是不完全的。


《二》

在上一章( Java中間件JMS之ActiveMQ入門http://blog.csdn.net/dengwanchuan/article/details/10241345)說到ActiveMQ能與spring進行整合,ActiveMQ與Spring進行整合有一定的好處,首先是可配置化,然後是能使用Spring的aop,tx等特性進行項目開發.

一.準備工作

我使用的是spring版本是4.0.0.M2,其他版本的也可以,只是配置不同,去Spring官網下載zip包,解開後將dist目錄下的所有jar包(根據自己選擇)拷貝到項目lib目錄下並加入項目項目中的lib中,一般jms所需要的Spring的jar有:


二.代碼開發

1.在src目錄下新建applicationContext.xml文件並輸入一下內容:

[html] view plaincopy
 
  1. <?xml version="1.0" encoding="GBK"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.            http://www.springframework.org/schema/beans/spring-beans.xsd">  
  6. </beans>  

 

2.引入spring,打開web.xml並將其內容修改爲以下內容:

[html] view plaincopy
 
  1. <?xml version="1.0" encoding="GBK"?>  
  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  5.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  6.     <context-param>  
  7.         <param-name>contextConfigLocation</param-name>  
  8.         <param-value>classpath*:applicationContext*.xml</param-value>  
  9.     </context-param>  
  10.     <servlet>  
  11.         <servlet-name>spring</servlet-name>  
  12.         <servlet-class>  
  13.             org.springframework.web.servlet.DispatcherServlet  
  14.         </servlet-class>  
  15.         <load-on-startup>1</load-on-startup>  
  16.     </servlet>  
  17.     <servlet-mapping>  
  18.         <servlet-name>spring</servlet-name>  
  19.         <url-pattern>/</url-pattern>  
  20.     </servlet-mapping>  
  21.     <welcome-file-list>  
  22.         <welcome-file>index.jsp</welcome-file>  
  23.     </welcome-file-list>  
  24. </web-app>  

3.配置JMSTemplate模板

 

類似於jdbcTemplate,首先要配置一個ConnectionFactory,之後要開始配置JmsTemplate模板了。最後是配置消息目標了。消息分爲隊列(Queue)和主題(Topic)兩大類。在applicationContext.xml中加入如下內容:

 

[html] view plaincopy
 
  1. <!-- 配置JMS連接工廠 -->  
  2.     <bean id="connectionFactory"  
  3.         class="org.apache.activemq.ActiveMQConnectionFactory">  
  4.         <property name="brokerURL" value="tcp://localhost:61616" />  
  5.     </bean>  
  6.     <!-- 發送消息的目的地(隊列) -->  
  7.     <bean id="queueDest"  
  8.         class="org.apache.activemq.command.ActiveMQQueue">  
  9.         <!-- 設置消息隊列的名字 -->  
  10.         <constructor-arg index="0" value="myQueue" />  
  11.     </bean>  
  12.     <!-- 配置Jms模板  -->  
  13.     <bean id="jmsQueueTemplate"  
  14.         class="org.springframework.jms.core.JmsTemplate">  
  15.         <property name="connectionFactory" ref="connectionFactory" />  
  16.         <property name="defaultDestination" ref="queueDest" />  
  17.         <!--<property name="receiveTimeout" value="10000" />  -->  
  18.     </bean>  
  19.       
  20.     <!-- 發送消息的目的地(主題) -->  
  21.     <bean id="topicDest"  
  22.         class="org.apache.activemq.command.ActiveMQTopic">  
  23.         <!-- 設置消息隊列的名字 -->  
  24.         <constructor-arg index="0" value="myTopic" />  
  25.     </bean>  
  26.     <!-- 配置TopicJms模板  -->  
  27.     <bean id="jmsTopicTemplate"  
  28.         class="org.springframework.jms.core.JmsTemplate">  
  29.         <property name="connectionFactory" ref="connectionFactory" />  
  30.         <property name="defaultDestination" ref="topicDest" />  
  31.         <!-- 配置是否爲發佈訂閱者模式,默認爲false -->  
  32.         <property name="pubSubDomain" value="true"/>  
  33.     <!--<property name="receiveTimeout" value="10000" />  -->  
  34.     </bean>  

 

receiveTimeout表示接收消息時的超時時間,設置的爲10秒,因爲如果不設置的話,加入接收消息時是阻塞着的,那麼將一直阻塞下去。配置完成了。但是我不建議設置這個時間,如果到達設置時間之後,生產者沒有運行,消費者接受到Message對象爲null,測試可能會出現異常,而且消費者將停止接受消息.那麼如何使用JmsTemplate發送消息呢?

spring的beanfactory得到一個jmsTemplate的實例和消息目標的實例,發送消息,夠簡單的吧。首先我們還從queue方式開始。下面我們就來編寫具體代碼。

4、編寫Point-to-Point (點對點)代碼

新建生產者類QueueProducerService.java,代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.jms.Destination;  
  6. import javax.jms.JMSException;  
  7. import javax.jms.Message;  
  8. import javax.jms.Session;  
  9. import javax.jms.TextMessage;  
  10. import org.springframework.jms.core.JmsTemplate;  
  11. import org.springframework.jms.core.MessageCreator;  
  12.   
  13. public class QueueProducerService{  
  14.     JmsTemplate jmsTemplate;  
  15.   
  16.     Destination destination;  
  17.   
  18.     public void send() {  
  19.         MessageCreator messageCreator = new MessageCreator() {  
  20.             public Message createMessage(Session session) throws JMSException {  
  21.                 TextMessage message = session.createTextMessage();  
  22.                 message.setText("QueueProducerService發送消息"+new Date());  
  23.                 return message;  
  24.             }  
  25.   
  26.         };  
  27.         jmsTemplate.send(this.destination,messageCreator);  
  28.     }  
  29.   
  30.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  31.         this.jmsTemplate = jmsTemplate;  
  32.     }  
  33.       
  34.     public void setDestination(Destination destination) {  
  35.         this.destination = destination;  
  36.     }  
  37. }  

 

生產者編寫完了,下面我們來編寫消費者,上面說了,發送消息的時候,spring的beanfactory得到一個jmsTemplate的實例和消息目標的實例,然後發送,那麼接受的時候肯定也是得到一個jmsTemplate的實例和消息目標的實例,然後接受,下面我們來看具體代碼。

新建一個消費者類QueueConsumerService.java,具體代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.Destination;  
  4. import javax.jms.JMSException;  
  5. import javax.jms.TextMessage;  
  6.   
  7. import org.springframework.jms.core.JmsTemplate;  
  8.   
  9.   
  10. public class QueueConsumerService{  
  11.   
  12.     JmsTemplate jmsTemplate;  
  13.   
  14.     Destination destination;  
  15.   
  16.     public void receive() {  
  17.         TextMessage message = (TextMessage) jmsTemplate.receive();  
  18.         try {  
  19.             System.out.println("QueueConsumerService收到消息:"+message.getText());  
  20.         } catch (JMSException e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.     }  
  24.   
  25.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  26.         this.jmsTemplate = jmsTemplate;  
  27.     }  
  28.   
  29.     public void setDestination(Destination destination) {  
  30.         this.destination = destination;  
  31.     }  
  32. }  

 

代碼編寫完畢,下面要進行bean的配置,在applicationContext.xml中加入如下代碼實例化對象和依賴注入:

 

[html] view plaincopy
 
  1. <bean id="queueProducerService" class="jms.mq.spring.QueueProducerService">  
  2.     <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  3.     <property name="destination" ref="queueDest" />  
  4. </bean>  
  5.   
  6. <bean id="queueConsumerService" class="jms.mq.spring.QueueConsumerService">  
  7.     <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  8.     <property name="destination" ref="queueDest" />  
  9. </bean>  

 

需要的業務代碼都已編寫完畢,下面編寫測試代碼。新建一個生產者的測試類QueueProducerTest.java。具體代碼如下:

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class QueueProducerTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void send() {  
  10.         QueueProducerService producerService = (QueueProducerService) appContext.getBean("queueProducerService");  
  11.         producerService.send();  
  12.     }  
  13.   
  14.     /** 
  15.      * @param args 
  16.      */  
  17.     public static void main(String[] args) {  
  18.         send();  
  19.     }  
  20.   
  21. }  

再建一個消費者的測試類,QueueConsumerTest.java,具體代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class QueueConsumerTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void receive() {  
  10.         QueueConsumerService consumerService = (QueueConsumerService) appContext.getBean("queueConsumerService");  
  11.         consumerService.receive();  
  12.     }  
  13.   
  14.     public static void main(String[] args) {  
  15.         receive();  
  16.     }  
  17.   
  18. }  

 

 

5、運行point-point(點對點)程序

所有代碼都編寫完了,我們來看一下我們的勞動成果。運行生產者測試類。控制檯打印出如下內容,畫線標註的就是我們發送的內容:

6、編寫Publisher/Subscriber(發佈/訂閱者)代碼

新建發佈者TopicPublisherService.java,內容如下:

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.jms.Destination;  
  6. import javax.jms.JMSException;  
  7. import javax.jms.MapMessage;  
  8. import javax.jms.Message;  
  9. import javax.jms.Session;  
  10. import javax.jms.TextMessage;  
  11.   
  12. import org.springframework.jms.core.JmsTemplate;  
  13. import org.springframework.jms.core.MessageCreator;  
  14.   
  15. import jms.spring.QueueProducerService;  
  16.   
  17. public class TopicPublisherService{  
  18.     JmsTemplate jmsTemplate;  
  19.        
  20.     Destination destination;  
  21.  
  22.     public void send() {  
  23.         MessageCreator messageCreator = new MessageCreator() {  
  24.             public Message createMessage(Session session) throws JMSException {  
  25.                 TextMessage message = session.createTextMessage();  
  26.                 message.setText("QueueProducerService發送消息"+new Date());  
  27.                 return message;  
  28.             }  
  29.         };  
  30.         jmsTemplate.send(this.destination,messageCreator);  
  31.   
  32.     }  
  33.   
  34.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  35.         this.jmsTemplate = jmsTemplate;  
  36.     }  
  37.   
  38.     public void setDestination(Destination destination) {  
  39.         this.destination = destination;  
  40.     }  
  41.   
  42. }  

再新建一個訂閱者TopicSubscriberService.java,代碼如下。

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.Destination;  
  4. import javax.jms.JMSException;  
  5. import javax.jms.TextMessage;  
  6.   
  7. import org.springframework.jms.core.JmsTemplate;  
  8.   
  9. import jms.spring.QueueConsumerService;  
  10.   
  11. public class TopicSubscriberService{  
  12.   
  13.     JmsTemplate jmsTemplate;  
  14.   
  15.     Destination destination;  
  16.   
  17.     public void receive() {  
  18.         TextMessage message = (TextMessage) jmsTemplate.receive();  
  19.         try {  
  20.             System.out.println("QueueConsumerService收到消息:"+message.getText());  
  21.         } catch (JMSException e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.     }  
  25.   
  26.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  27.         this.jmsTemplate = jmsTemplate;  
  28.     }  
  29.   
  30.     public void setDestination(Destination destination) {  
  31.         this.destination = destination;  
  32.     }  
  33. }  

在配置文件中applicationContext.xml增加如下配置:

[html] view plaincopy
 
  1. <span style="white-space:pre">  </span><bean id="topicPublisherService" class="jms.mq.spring.TopicPublisherService">  
  2.        <property name="jmsTemplate" ref="jmsTopicTemplate"/>  
  3.        <property name="destination" ref="topicDest"/>  
  4.     </bean>  
  5.    
  6.     <bean id="topicSubscriberService" class="jms.mq.spring.TopicSubscriberService">  
  7.        <property name="jmsTemplate" ref="jmsTopicTemplate"/>  
  8.        <property name="destination" ref="topicDest"/>  
  9.     </bean>  

 

編寫測試程序發佈者測試類,TopicPublisherTest.java

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class TopicPublisherTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void send() {  
  10.         TopicPublisherService topicPublisherService = (TopicPublisherService) appContext.getBean("topicPublisherService");  
  11.         topicPublisherService.send();  
  12.     }  
  13.   
  14.     public static void main(String[] args) {  
  15.         send();  
  16.     }  
  17. }  

編寫測試程序訂閱者測試類,TopicSubscriberTest.java

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class TopicSubscriberTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void receive() {  
  10.         TopicSubscriberService topicSubscriberService = (TopicSubscriberService) appContext.getBean("topicSubscriberService");  
  11.         topicSubscriberService.receive();  
  12.     }  
  13.   
  14.     public static void main(String[] args) {  
  15.         receive();  
  16.     }  
  17. }  

7.Publisher/Subscriber(發佈/訂閱者)程序

先運行訂閱者,再運行發佈者,可以看到訂閱者能打印信息;但是反之就不行,這就是Publisher/Subscriber(發佈/訂閱者)的特性;

跟Point-Point(點對點)對比的話,不管運行生存者還是消費者,都會打印信息,可以閱讀前一章http://blog.csdn.net/dengwanchuan/article/details/10241345瞭解這兩種模式的區別和聯繫。

 

 

附加完整的applicationContext.xml配置文件

 

[html] view plaincopy
 
  1. <?xml version="1.0" encoding="GBK"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.            http://www.springframework.org/schema/beans/spring-beans.xsd">  
  6.     <!-- 配置JMS連接工廠 -->  
  7.     <bean id="connectionFactory"  
  8.         class="org.apache.activemq.ActiveMQConnectionFactory">  
  9.         <property name="brokerURL" value="tcp://localhost:61616" />  
  10.     </bean>  
  11.     <!-- 發送消息的目的地(隊列) -->  
  12.     <bean id="queueDest"  
  13.         class="org.apache.activemq.command.ActiveMQQueue">  
  14.         <!-- 設置消息隊列的名字 -->  
  15.         <constructor-arg index="0" value="myQueue" />  
  16.     </bean>  
  17.     <!-- 配置Jms模板  -->  
  18.     <bean id="jmsQueueTemplate"  
  19.         class="org.springframework.jms.core.JmsTemplate">  
  20.         <property name="connectionFactory" ref="connectionFactory" />  
  21.         <property name="defaultDestination" ref="queueDest" />  
  22.         <!--<property name="receiveTimeout" value="10000" />  -->  
  23.     </bean>  
  24.       
  25.     <!-- 發送消息的目的地(主題) -->  
  26.     <bean id="topicDest"  
  27.         class="org.apache.activemq.command.ActiveMQTopic">  
  28.         <!-- 設置消息隊列的名字 -->  
  29.         <constructor-arg index="0" value="myTopic" />  
  30.     </bean>  
  31.     <!-- 配置TopicJms模板  -->  
  32.     <bean id="jmsTopicTemplate"  
  33.         class="org.springframework.jms.core.JmsTemplate">  
  34.         <property name="connectionFactory" ref="connectionFactory" />  
  35.         <property name="defaultDestination" ref="topicDest" />  
  36.         <!-- 配置是否爲發佈訂閱者模式,默認爲false -->  
  37.         <property name="pubSubDomain" value="true"/>  
  38.     <!--<property name="receiveTimeout" value="10000" />  -->  
  39.     </bean>  
  40.       
  41.     <bean id="queueProducerService" class="jms.mq.spring.QueueProducerService">  
  42.         <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  43.         <property name="destination" ref="queueDest" />  
  44.     </bean>  
  45.   
  46.     <bean id="queueConsumerService" class="jms.mq.spring.QueueConsumerService">  
  47.         <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  48.         <property name="destination" ref="queueDest" />  
  49.     </bean>  
  50.       
  51.       
  52.     <bean id="topicPublisherService" class="jms.mq.spring.TopicPublisherService">  
  53.        <property name="jmsTemplate" ref="jmsTopicTemplate"/>  
  54.        <property name="destination" ref="topicDest"/>  
  55.     </bean>  
  56.    
  57.     <bean id="topicSubscriberService" class="jms.mq.spring.TopicSubscriberService">  
  58.        <property name="jmsTemplate" ref="jmsTopicTemplate"/>  
  59.        <property name="destination" ref="topicDest"/>  
  60.     </bean>  
  61. </beans>  


《三》

對於讓spring管理監聽的實現方式有兩種方法,一種是自己寫監聽器,然後交給spring的監聽適配器管理,再由監聽容器管理監聽適配器,另一種是寫一個實現MessageListener接口的類。第一種在第一章涉及到,但是沒有交給spring託管.其實實現的方法很簡單,在j2ee mvc模式中,用到spring的aop特性.這章講第

二種方法,這樣消費者可以時時接受到生產者的消息,訂閱者可以時時接受到發佈者的消息.不用循環接受。

 

1.Queue(點對點)方式

 

消費者QueueConsumerMessageListener類,具體如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.JMSException;  
  4. import javax.jms.Message;  
  5. import javax.jms.MessageListener;  
  6. import javax.jms.TextMessage;  
  7.   
  8. public class QueueConsumerMessageListener implements MessageListener{  
  9.     public void onMessage(Message msg) {  
  10.         if(msg instanceof TextMessage){  
  11.             TextMessage textMessage = (TextMessage) msg;  
  12.             try {  
  13.                 System.out.println("--隊列 MessageListener收到信息:"+textMessage.getText());  
  14.             } catch (JMSException e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.         }  
  18.     }  
  19. }  

 

 

2.Topic(發佈/訂閱)方式

訂閱者實現類TopicSubscriberMessageListener類,具體如下:

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.JMSException;  
  4. import javax.jms.Message;  
  5. import javax.jms.MessageListener;  
  6. import javax.jms.TextMessage;  
  7.   
  8. public class TopicSubscriberMessageListener implements MessageListener{  
  9.     public void onMessage(Message msg) {  
  10.         if(msg instanceof TextMessage){  
  11.             TextMessage textMessage = (TextMessage) msg;  
  12.             try {  
  13.                 System.out.println("--訂閱者 MessageListener收到信息:"+textMessage.getText());  
  14.             } catch (JMSException e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.         }  
  18.     }  
  19. }  



3.修改配置文件

 

[html] view plaincopy
 
  1. <!-- 實現接口的方式 -->  
  2.     <bean id="queueConsumerMessageListener" class="jms.mq.spring.QueueConsumerMessageListener">  
  3.     </bean>  
  4.     <!-- 新增一個隊列地址名字不能跟上面的重複 -->  
  5.     <bean id="queueConsumerMessageListenerDest" class="org.apache.activemq.command.ActiveMQQueue">  
  6.         <constructor-arg index="0" value="myMessageListenerQueue" />  
  7.     </bean>  
  8.     <bean id="myMsgQueuelistenerContainer"  
  9.         class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  10.         <property name="connectionFactory" ref="connectionFactory" />  
  11.         <property name="destination" ref="queueConsumerMessageListenerDest" />  
  12.         <property name="messageListener" ref="queueConsumerMessageListener" />  
  13.         <property name="receiveTimeout" value="10000" />  
  14.     </bean>  
  15.   
  16.     <bean id="topicSubscriberMessageListener" class="jms.mq.spring.TopicSubscriberMessageListener"></bean>  
  17.     <bean id="topicSubscriberMessageListenerDest" class="org.apache.activemq.command.ActiveMQTopic">  
  18.         <constructor-arg index="0" value="myMessageListenerTopic" />  
  19.     </bean>  
  20.     <bean id="myMsgTopiclistenerContainer"  
  21.         class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  22.         <property name="connectionFactory" ref="connectionFactory" />  
  23.         <property name="destination" ref="topicSubscriberMessageListenerDest" />  
  24.         <property name="messageListener" ref="topicSubscriberMessageListener" />  
  25.         <property name="pubSubDomain" value="true" />  
  26.         <property name="receiveTimeout" value="10000" />  
  27.     </bean>  


順便將上一章的配置也改一下

 

[html] view plaincopy
 
  1. <bean id="queueProducerService" class="jms.mq.spring.QueueProducerService">  
  2.         <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  3.         <!--<property name="destination" ref="queueDest" /> -->  
  4.         <property name="destination" ref="queueConsumerMessageListenerDest" />  
  5.     </bean>  
  6.   
  7.     <bean id="queueConsumerService" class="jms.mq.spring.QueueConsumerService">  
  8.         <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  9.         <property name="destination" ref="queueDest" />   
  10.     </bean>  
  11.   
  12.   
  13.     <bean id="topicPublisherService" class="jms.mq.spring.TopicPublisherService">  
  14.         <property name="jmsTemplate" ref="jmsTopicTemplate" />  
  15.         <!-- <property name="destination" ref="topicDest" /> -->  
  16.         <property name="destination" ref="topicSubscriberMessageListenerDest" />  
  17.     </bean>  
  18.   
  19.     <bean id="topicSubscriberService" class="jms.mq.spring.TopicSubscriberService">  
  20.         <property name="jmsTemplate" ref="jmsTopicTemplate" />  
  21.         <property name="destination" ref="topicDest" />  
  22.     </bean>  

 

注意是修改 bean id爲queueProducerService和的topicPublisherService的destination

 

4.運行

Queue(點對點)方式,只運行QueueProducerTest.java,結果如下

 

Topic(發佈/訂閱)方式,只運行TopicPublisherTest.java,結果如下

 

ok,如果Queue(點對點)方式和Topic(發佈/訂閱)方式運行的結果如上圖所示,說明你成功了.





《四》

前幾章都是直接發送MapMessage類型的數據,拿前面的例子來講,如果生產者發送的是TextMessage,消費者也是必須TextMessage;如果我們自己要發送的數據不是TextMessage類型,而消費者還是TextMessage的,那該怎麼辦?難道每次接受後都要增加一個轉換方法麼?其實spring早就考慮到這種情況了。轉化器在很多組件中都是必不缺少的東西Spring的MessageConverter接口提供了對消息轉換的支持。

 

1、轉換類的相關代碼POJO

新建一個類MsgPoJo,就是一個簡單的Pojo類。具體代碼如下:

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class MsgPoJo implements Serializable{  
  6.     private String id;  
  7.     private String text;  
  8.     public String getId() {  
  9.         return id;  
  10.     }  
  11.     public void setId(String id) {  
  12.         this.id = id;  
  13.     }  
  14.     public String getText() {  
  15.         return text;  
  16.     }  
  17.     public void setText(String text) {  
  18.         this.text = text;  
  19.     }     
  20. }  

 

2.轉換類的實現

新建一個類MsgConverter.java,實現MessageConverter接口。生成的代碼如下

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.JMSException;  
  4. import javax.jms.Message;  
  5. import javax.jms.Session;  
  6. import javax.jms.TextMessage;  
  7.   
  8. import org.springframework.jms.support.converter.MessageConversionException;  
  9. import org.springframework.jms.support.converter.MessageConverter;  
  10.   
  11. public class MsgConverter implements MessageConverter{  
  12.   
  13.     @Override  
  14.     public Object fromMessage(Message message) throws JMSException,  
  15.     MessageConversionException {  
  16.         if (!(message instanceof TextMessage)) {  
  17.             throw new MessageConversionException("Message is not TextMessage");  
  18.         }  
  19.         System.out.println("--轉換接收的消息--");  
  20.         TextMessage textMessage = (TextMessage) message;  
  21.         MsgPoJo msgPojo = new MsgPoJo();  
  22.         String[] texts=textMessage.getText().split(",");  
  23.         msgPojo.setId(texts[0]);  
  24.         msgPojo.setText(texts[1]);  
  25.         return msgPojo;  
  26.     }  
  27.   
  28.     @Override  
  29.     public Message toMessage(Object object, Session session) throws JMSException,  
  30.     MessageConversionException {  
  31.         if (!(object instanceof MsgPoJo)) {  
  32.             throw new MessageConversionException("obj is not MsgPojo");  
  33.         }  
  34.         System.out.println("--轉換髮送的消息--");  
  35.         MsgPoJo msgPojo = (MsgPoJo) object;  
  36.         TextMessage textMessage = session.createTextMessage();  
  37.         textMessage.setText(msgPojo.getId()+","+msgPojo.getText());  
  38.         return  textMessage;  
  39.     }  
  40. }  

 

 

代碼很簡單就是做些轉換,有fromMessage和toMessage兩個方法,真好對應發送轉換toMessage和接受轉換fromMessage。此時,發送和接收消息要換成template.convertAndSend(message);template.receiveAndConvert()。接下來我做一些配置,讓spring知道我們的轉換類。修改applicationContext.xml中jms模版配置的代碼,修改後的代碼如下:

[html] view plaincopy
 
  1. <!-- 類轉換 -->  
  2. <bean id="msgConverter" class="jms.mq.spring.MsgConverter"></bean>  
  3.   
  4. <!-- 配置Jms模板 -->  
  5. <bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">  
  6.     <property name="connectionFactory" ref="connectionFactory" />  
  7.     <property name="defaultDestination" ref="queueDest" />  
  8.     <!--<property name="receiveTimeout" value="10000" /> -->  
  9.     <!-- 類轉換 -->  
  10.     <property name="messageConverter" ref="msgConverter"></property>  
  11. </bean>  

注意:如果你有隊列監聽容器配置,配置jmsQueueTemplate和jmsTopicTemplate可能與隊列容器配置衝突。

3、業務相關代碼和配置

在QueueProducerService.java增加convertAndSend()方法並在其實現類中實現,實現類的代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.jms.Destination;  
  6. import javax.jms.JMSException;  
  7. import javax.jms.Message;  
  8. import javax.jms.Session;  
  9. import javax.jms.TextMessage;  
  10. import org.springframework.jms.core.JmsTemplate;  
  11. import org.springframework.jms.core.MessageCreator;  
  12.   
  13. public class QueueProducerService{  
  14.     JmsTemplate jmsTemplate;  
  15.   
  16.     Destination destination;  
  17.   
  18.     public void send() {  
  19.         MessageCreator messageCreator = new MessageCreator() {  
  20.             public Message createMessage(Session session) throws JMSException {  
  21.                 TextMessage message = session.createTextMessage();  
  22.                 message.setText("QueueProducerService發送消息"+new Date());  
  23.                 return message;  
  24.             }  
  25.   
  26.         };  
  27.         jmsTemplate.send(this.destination,messageCreator);  
  28.     }  
  29.       
  30.     public void convertAndSend(){  
  31.         MsgPoJo msgPojo = new MsgPoJo();  
  32.         msgPojo.setId("1");  
  33.         msgPojo.setText("first msg");  
  34.         System.out.println("--發送消息:msgPojo.id爲"+msgPojo.getId()+";msgPojo.text爲"+msgPojo.getText());  
  35.         jmsTemplate.convertAndSend(this.destination, msgPojo);  
  36.     }  
  37.   
  38.   
  39.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  40.         this.jmsTemplate = jmsTemplate;  
  41.     }  
  42.       
  43.     public void setDestination(Destination destination) {  
  44.         this.destination = destination;  
  45.     }  
  46. }  

 

同樣在QueueConsumerService.java中增加receiveAndConvert()方法並在其實現類中實現,實現類的代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import javax.jms.Destination;  
  4. import javax.jms.JMSException;  
  5. import javax.jms.TextMessage;  
  6. import org.springframework.jms.core.JmsTemplate;  
  7.   
  8.   
  9. public class QueueConsumerService{  
  10.   
  11.     JmsTemplate jmsTemplate;  
  12.   
  13.     Destination destination;  
  14.   
  15.     public void receive() {  
  16.         TextMessage message = (TextMessage) jmsTemplate.receive();  
  17.         try {  
  18.             System.out.println("QueueConsumerService收到消息:"+message.getText());  
  19.   
  20.         } catch (JMSException e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.     }  
  24.   
  25.     public void receiveAndConvert() {  
  26.         MsgPoJo msgPojo = (MsgPoJo)jmsTemplate.receiveAndConvert();  
  27.         if(msgPojo!=null){  
  28.             System.out.println("--收到消息:msgPojo.id爲"+msgPojo.getId()+";msgPojo.text爲"+msgPojo.getText());  
  29.         }  
  30.     }  
  31.   
  32.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  33.         this.jmsTemplate = jmsTemplate;  
  34.     }  
  35.   
  36.     public void setDestination(Destination destination) {  
  37.         this.destination = destination;  
  38.     }  
  39. }  

 

 

修改我們的兩個測試類,增加對轉換方法的調用,不再贅述,直接上代碼:

QueueConsumerTest.java測試類

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class QueueConsumerTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void receive() {  
  10.         QueueConsumerService consumerService = (QueueConsumerService) appContext.getBean("queueConsumerService");  
  11.         consumerService.receive();  
  12.     }  
  13.   
  14.     private static void receiveAndConvert() {  
  15.         QueueConsumerService consumerService = (QueueConsumerService) appContext.getBean("queueConsumerService");  
  16.         consumerService.receiveAndConvert();  
  17.     }  
  18.   
  19.   
  20.     public static void main(String[] args) {  
  21.         //receive();  
  22.         receiveAndConvert();  
  23.     }  
  24. }  

QueueProducerTest.java測試類

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class QueueProducerTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void send() {  
  10.         QueueProducerService producerService = (QueueProducerService) appContext.getBean("queueProducerService");  
  11.         producerService.send();  
  12.     }  
  13.       
  14.     private static void convertAndSend() {  
  15.         QueueProducerService producerService = (QueueProducerService) appContext.getBean("queueProducerService");  
  16.         producerService.convertAndSend();  
  17.     }  
  18.   
  19.     public static void main(String[] args) {  
  20.         //send();  
  21.         convertAndSend();  
  22.     }  
  23.   
  24. }  

 

代碼編寫完畢,我們看一下我們的勞動成果。首先運行生產者類和消費者控制檯信息如下:

 

收到的內容與發的內容相同,說明轉換成功了。如果這一部分的程序使用的隊列跟上面的一樣,那你會發現發送的時候打印出的信息不值上面的一個,還包括一個接收的信息,這是爲什麼呢?瞭解spring原理的人應該知道,spring是把所有類都加載到內容中,當然也包括我們上門寫的按個實現MessageListener的一個消費者類,他們也在運行,如果監聽的地址跟你送的地址正好相同的話,他也有可能收到這個信息。所以在測試的時候要注意修改配置文件。

 

[html] view plaincopy
 
  1. <bean id="queueProducerService" class="jms.mq.spring.QueueProducerService">  
  2.     <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  3.     <property name="destination" ref="queueDest" />    
  4. </bean>  
  5.   
  6. <bean id="queueConsumerService" class="jms.mq.spring.QueueConsumerService">  
  7.     <property name="jmsTemplate" ref="jmsQueueTemplate" />  
  8.     <property name="destination" ref="queueDest" />   
  9. </bean>  

 

4、監聽器上的使用方式

我再來學習一下跟監聽器聯合使用的方式,只在發佈訂閱者模式上演示一下。我們先來修改發佈者的實現方式,在發佈者中增加convertAndSend方法並在其實現類中實現,訂閱者監聽器沒有類轉換,不用修改,發佈者修改後的代碼如下:

 

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.jms.Destination;  
  6. import javax.jms.JMSException;  
  7. import javax.jms.MapMessage;  
  8. import javax.jms.Message;  
  9. import javax.jms.Session;  
  10. import javax.jms.TextMessage;  
  11.   
  12. import org.springframework.jms.core.JmsTemplate;  
  13. import org.springframework.jms.core.MessageCreator;  
  14.   
  15. import jms.spring.QueueProducerService;  
  16.   
  17. public class TopicPublisherService{  
  18.     JmsTemplate jmsTemplate;  
  19.        
  20.     Destination destination;  
  21.   
  22.     public void send() {  
  23.         MessageCreator messageCreator = new MessageCreator() {  
  24.   
  25.             public Message createMessage(Session session) throws JMSException {  
  26.                 TextMessage message = session.createTextMessage();  
  27.                 message.setText("QueueProducerService發送消息"+new Date());  
  28.                 return message;  
  29.             }  
  30.         };  
  31.         jmsTemplate.send(this.destination,messageCreator);  
  32.     }  
  33.   
  34.     public void convertAndSend(Object obj) {  
  35.         System.out.println("--發送PoJo對象...");  
  36.         jmsTemplate.convertAndSend(destination, obj);  
  37.     }  
  38.   
  39.       
  40.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  41.         this.jmsTemplate = jmsTemplate;  
  42.     }  
  43.   
  44.     public void setDestination(Destination destination) {  
  45.         this.destination = destination;  
  46.     }  
  47.   
  48. }  

 

發佈訂閱者配置文件如下

 

[html] view plaincopy
 
  1. <!-- 配置TopicJms模板 -->  
  2. <bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">  
  3.     <property name="connectionFactory" ref="connectionFactory" />  
  4.     <property name="defaultDestination" ref="topicDest" />  
  5.     <!-- 配置是否爲發佈訂閱者模式,默認爲false -->  
  6.     <property name="pubSubDomain" value="true" />  
  7.     <!--<property name="receiveTimeout" value="10000" /> -->  
  8.     <property name="messageConverter" ref="msgConverter"></property>  
  9. </bean>  
[html] view plaincopy
 
  1. <bean id="topicPublisherService" class="jms.mq.spring.TopicPublisherService">  
  2.         <property name="jmsTemplate" ref="jmsTopicTemplate" />  
  3.         <!-- <property name="destination" ref="topicDest" /> -->  
  4.         <property name="destination" ref="topicSubscriberMessageListenerDest" />   
  5.     </bean>  
  6.   
  7.     <bean id="topicSubscriberService" class="jms.mq.spring.TopicSubscriberService">  
  8.         <property name="jmsTemplate" ref="jmsTopicTemplate" />  
  9.         <property name="destination" ref="topicDest" />  
  10.     </bean>  

 

修改上面的發佈測試類,修改增加對新增方法的調用,修改後的內容如下:

[java] view plaincopy
 
  1. package jms.mq.spring;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  5.   
  6. public class TopicPublisherTest {  
  7.     private static ApplicationContext appContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  8.   
  9.     private static void send() {  
  10.         TopicPublisherService topicPublisherService = (TopicPublisherService) appContext.getBean("topicPublisherService");  
  11.         topicPublisherService.send();  
  12.     }  
  13.     private static void convertAndSend() {  
  14.         TopicPublisherService topicPublisherService = (TopicPublisherService) appContext.getBean("topicPublisherService");  
  15.         MsgPoJo msgPoJo = new MsgPoJo();  
  16.         msgPoJo.setId("1");  
  17.         msgPoJo.setText("測試內容");  
  18.         topicPublisherService.convertAndSend(msgPoJo);  
  19.     }  
  20.   
  21.   
  22.     public static void main(String[] args) {  
  23.         //send();  
  24.         convertAndSend();  
  25.     }  
  26. }  

運行發佈測試類,運行結果如下:

寫在到這裏,ActiveMQ與spring整合就講完了,主要講了ActiveMQ與spring的簡單整合,監聽器和類轉換這些主要功能.

呵呵,寫到不好,請大家不要拍磚。



《五》

前面幾章講了ActiveMQ以及ActiveMQ與spring的整合,這是jms的ActiveMQ實現。但是jms到底是什麼呢。如果開始就講解這些理論比較強的東西,可能比較難理解,但是現在結合前面幾章的實例對比就比較容易上手了。

 

1、.JMS 的概念

 

    JMS(java Message Service),即 Java 消息服務,是一組 Java 應用程序接口,用以提供創建、發接、接收、讀取消息的服務。SUN 只提供接口,由不同的廠商根據該接口提供自己的實現。

 

2、.JMS 的目標

   更好地理解 JMS 有助於瞭解 JMS 規範的制定者設置的目標。現在,市場上有許多企業消息產品,生產這些產品的幾家公司也參與了 JMS 的開發。

   現有的這些系統的能力和功能各不相同。這些制定者知道如果 JMS 結合所有現有系統的所有功能,那麼它會變得過於複雜。同樣,他們相信,他們也不能讓 JMS 只侷限於所有系統共有的那些特性。

   制定者相信,讓 JMS 包括實現“高級的企業應用程序”所需要的所有功能是很重要的。

   JMS 規範中聲明, JMS 的目標是:

  • 定義一組消息公用概念和實用工具。
  • 最少化程序員使用消息時必須學習的概念。
  • 最大化消息應用程序的可移植性。 
  • 最小化實現一個提供者所需的工作量。
  • 爲點對點和 pub/sub  提供客戶機接口。“域”是用於在前面討論的消息模型的 JMS 術語。(注意:提供者不需要實現兩個域。)

3、.JMS 中間件

  1).IBM MQ 系列產品提供的服務使得應用程序可以使用消息隊列進行交流,通過一系列基於 Java 的 API 提供了 MQseries 在 Java 中應用開發的方法。它支持點到點和發佈/訂閱兩種消息模式。


  2).WebLogic是BEA公司實現的基於工業標準的J2EE應用服務器,支持大多數企業級JavaAPI,它完全兼容 JMS 規範,支持點到點和發佈/訂閱消息模式,它支持消息的多點廣播、持久消息存儲的文件和數據庫、XML 消息,以及動態創建持久隊列和主題。


  3).JBoss 是 JBoss 公司開發的一個免費開源的應用服務器,它提供了 EJB 運行的環境,並能夠結合 EJB 進行 JMS 消息的收取,支持點到點模型和發佈/訂閱模型。

  4).ActiveMQ 是一個基於 Apache 2.0 Licenced 發佈的開放源代碼的 JMS 產品,它能夠提供點到點消息模式和發佈/訂閱消息模式,支持 JBoss、Geronimo 等開源應用服務器,支持 Spring 框架的消息驅動,新增了一個 P2P 傳輸層,可以用於創建可靠的 P2P JMS 網絡連接,擁有消息持久化、事務、集羣支持等 JMS 基礎設施服務。

  5).OpenJMS 是一個開源的 JMS 規範的實現,它支持點對點模型和發佈/訂閱模型,支持同步與異步消息發送、可視化管理界面,支持 Applet,能夠與 Jakarta Tomcat 這樣的 Servlet容器結合,支持 RMI、TCP、HTTP 與 SSL 協議。

 

4、.JMS特性

     在 JMS 之前,每一家 MOM 廠商都用專有 API 爲應用程序提供對其產品的訪問,通常可用於許多種語言,其中包括 Java 語言。JMS 通過 MOM 產品爲 Java 程序提供了一個發送和接收消息的標準的、便利的方法。用 JMS 編寫的程序可以在任何實現 JMS 標準的 MOM 上運行。JMS 是由 Sun Microsystems 開發的,它爲 Java 程序員提供一種訪問企業消息系統的方法,即 面向消息的中間件 (MOM)。MOM 通過中間媒介以間接方式在應用程序之間異步傳送數據,用這種方法提供一種以鬆散耦合的靈活方式集成應用程序的機制。

    要執行並測試這些程序,需要訪問 JMS 的供應商實現。大多數 Java 2 Enterprise Edition (J2EE)供應商都會提供 JMS 的一種實現(也就是上面說的JMS中間件)。要想獲得設置 JMS 運行時程序和執行程序的有關信息,請參閱供應商文檔。

    它爲 Java 程序提供一種訪問 企業消息系統 的方法。在討論 JMS 之前,我們分來析一下企業消息系統。

   企業消息系統,即 面向消息的中間件(MOM),提供了以鬆散耦合的靈活方式集成應用程序的一種機制。它們提供了基於 存儲和轉發 的應用程序之間的異步數據發送,即應用程序彼此不直接通信,而是與作爲中介的 MOM 通信。

MOM 提供了有保證的消息發送(至少是在儘可能地做到這一點),應用程序開發人員無需瞭解遠程過程調用(PRC)和網絡/通信協議的細節。

    1)消息靈活性

MOM 將消息路由給應用程序 B,這樣,消息就可以存在於完全不同的計算機上,MOM 負責處理網絡通信。如果網絡連接不可用, MOM 會存儲消息,直到連接變得可用時,再將消息轉發給應用程序 B。

靈活性的另一方面體現在,當應用程序 A 發送其消息時,應用程序 B 甚至可以不處於執行狀態。MOM 將保留這個消息,直到應用程序 B 開始執行並試着檢索消息爲止。這還防止了應用程序 A 因爲等待應用程序 B 檢索消息而出現阻塞。

這種異步通信要求應用程序的設計與現在大多數應用程序不同,不過,對於時間無關或並行處理,它可能是一個極其有用的方法。

    2)鬆散耦合

企業消息系統的真正威力在於應用程序的 鬆散耦合。在前一頁的圖表中,由應用程序 A 發送消息指定一個特定目標,如“訂單處理”。而現在,是由應用程序 B 提供訂單處理功能。

但是在將來,我們可以用不同的訂單處理程序替換應用程序 B,應用程序 A 將不再是明智之選。替換應用程序將繼續發送消息完成“訂單處理”,而消息也仍將得到處理。

同樣,我們也可以替換應用程序 A,只要替換應用程序繼續發送消息進行“訂單處理”,訂單處理程序就無需知道是否有一個新的應用程序在發送訂單。

5.JMS模式

最初,開發企業消息系統是爲了實現 點對點模型 (PTP),由一個應用程序生成的每個消息都由另一個應用程序接收。近年來,出現了一種新的模型,叫做 發佈與訂閱 (或者 pub/sub)。

Pub/sub 用稱爲 主題(topic) 的內容分層結構代替了 PTP 模型中的惟一目的地,發送應用程序 發佈 自己的消息,指出消息描述的是有關分層結構中的一個主題的信息。

希望接收這些消息的應用程序 訂閱了 這個主題。訂閱包含子主題的分層結構中的主題的訂閱者可以接收該主題和其子主題發表的所有消息。

多個應用程序可以就一個主題發佈和訂閱消息,而應用程序對其他人仍然是匿名的。MOM 起着 代理(broker) 的作用,將一個主題已發表的消息路由給該主題的所有訂閱者。

JMS 應用程序由以下元素組成:

  • JMS 客戶機。 用 JMS API 發送和接收消息的 Java 程序。
  • 非 JMS 客戶機。 認識到傳統程序通常整個 JMS 應用程序的一部分是非常重要的,在規劃時必須優先考慮它們的存在。
  • 消息。就JMS 應用程序設計而言,通過JMS 和非 JMS 客戶機所交換的消息的格式和內容是完整的。
  • JMS 提供者。 如前所述,JMS 定義了一組接口,供應者必須提供特定於其 MOM 產品的具體實現。
  • 管理對象。 消息系統提供者的管理員創建的、獨立於提供者的專有技術的對象。
 
6、.JMS管理對象

 

MOM 產品的提供者在實現消息時使用的機制和技術有很大不同。爲了保持 JMS 客戶機的可移植性,實現了 JMS 接口的對象必須與提供者的專有技術隔離。

完成這項任務的機制是 管理對象。這些實現 JMS 接口的對象由提供者的消息系統的管理員創建,並被放置在 JNDI 名字空間中。

然後由 JMS 程序檢索這些對象,通過它們實現的 JMS 接口訪問這些對象。JMS 提供者必須提供允許創建受管理對象及它們在 JNDI 名字空間中的存放地的工具。

有兩種受管理對象:

  • ConnectionFactory:用於創建到提供者的底層消息系統的連接。
  • Destination:用 JMS 客戶機來指定正發送消息的目的地或正接收消息的來源。

儘管受管理對象本身就是特定於提供者實現的類的例子,但可以使用可移植機制(JNDI)檢索它們,並且可以通過可移植接口(JMS)訪問它們。JMS 程序只需要知道管理對象的 JNDI 名稱和 JMS 接口類型即可,無需瞭解特定於提供者的知識。

 

7、.JMS接口

 

JMS 定義了一組封裝各種消息概念的高級接口。而這些接口又因爲兩個消息域——PTP 和 pub/sub——進行了進一步地定義和定製。

高級接口包括:

  • ConnectionFactory:一個創建 Connection 的受管理對象。
  • Connection:連接到提供者的活動連接。
  • Destination:一個封裝消息目的地的身份的受管理對象,如消息的來源地和發送地。
  • Session:發送和接收消息的單線程環境。爲了簡化,並且因爲 Session 控制事務的緣故,通過多個線程進行併發訪問受到了限制。可以將多個 Session 用於多線程應用程序。
  • MessageProducer:用於發送消息。
  • MessageConsumer:用於接收消息。

 

下表列出了從每一個高級接口繼承的特定於域的接口。

 

高級接口PTP 域Pub/sub 域
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiverQueueBrowser TopicSubscriber

 

在 JMS 以前的版本中,高級接口是特定於域的接口的父接口,並且只包含在兩個域中共有的那些功能。JMS 提供者沒有提供高級接口的實現。在 JMS 1.1 中,一些高級接口現在則被認爲是“公用接口”,並且它們包含兩個域的所有功能; JMS 提供者必須提供這些接口的實現。儘管公用接口仍然是特定於域的接口的父接口,但它們現在是 JMS 客戶機編程的首選方法,並且它們提供特定於域的接口只爲了後向兼容。

下面重新列出了前一小節中的表,展示了一些公用接口。

 

JMS 公用接口PTP 域Pub/sub 域
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver,QueueBrowser TopicSubscriber

 

統一有公用接口的域會導致繼承這些方法的一些特定於域的類不再適合它的域。如果在客戶機代碼中出現這種情況,JMS 提供者需要使用 IllegalStateException。

 

8、jms開發

 

一個典型的 JMS 程序要經過以下步驟才能開始產生和使用消息:

  1. 通過 JNDI 查詢 ConnectionFactory 。
  2. 通過 JNDI 查詢一個或者多個 Destination
  3. 用 ConnectionFactory 創建一個 Connection
  4. 用 Connection 創建一個或者多個 Session
  5. 用 Session 和 Destination 創建所需要的 MessageProducer 和 MessageConsumer
  6. 啓動 Connection

這時,消息就可以開始流動,應用程序可以根據需要接收、處理和發送消息。 在後面幾節中,我們將開發 JMS 程序,您將會看到這些步驟的細節。

 

9、jms消息


 

消息系統的核心當然是消息。JMS 爲不同類型的內容提供了幾種消息類型,但所有消息都是從 Message 接口派生出來的。

Message 分爲三個組成部分:

  • header 是一組標準字段,客戶機和提供者都用它們來標識和路由消息。
  • Properties 提供了一個給消息添加可選標題字段的實用工具。如果應用程序需要用標準標題字段沒有提供的方法對消息進行歸類或分類,那麼可以爲消息添加一個屬性來實現這種歸類和分類;提供了 set<Type>Property(...) 和 get<Type>Property(...) 方法來設置和獲得各種 Java 類型的屬性,其中包括 Object。JMS 定義了提供者可以選擇性提供的一組標準屬性。
  • 消息的 body 包含將 發送到接收應用程序的內容。每一個消息接口都專用於它所支持的內容類型。
 

10、header屬性

 

下面列出了 Message 的每一個標題字段的名稱、它對應的 Java 類型和字段的描述:

  • JMSMessageID——類型爲 string

    惟一標識提供者發送的每一條消息。這個字段是在發送過程中由提供者設置的,客戶機只能在消息發送後才能確定消息的 JMSMessageID

  • JMSDestination——類型爲 Destination

    消息發送的 Destination,在發送過程中由提供者設置。

  • JMSDeliveryMode——類型爲 int

    包含值 DeliveryMode.PERSISTENT 或者 DeliveryMode.NON_PERSISTENT。持久性消息被傳輸並且只被傳輸一次,非持久性消息最多被傳輸一次。要知道“最多一次”包括根本不傳輸。非持久性消息在應用程序或者系統出故障時被提供者弄丟。因此要格外小心,確保持久性消息不受故障的影響。這比開銷通常被認爲是發送持久性消息方面的開銷,在決定消息的發送模式時,必須仔細考慮,在可靠性和性能之間進行權衡。

  • JMSTimestamp——類型爲 long

    提供者發送消息的時間,由提供者在發送過程中設置。

  • JMSExpiration——類型爲 long

    消息失效的時間。這個值是在發送過程中計算的,是發送方法的生存時間(time-to-live)值和當前時間值的和。提供者不應發送過期的消息。值 0 表明消息不會過期。

  • JMSPriority——類型爲 int

    消息的優先級,由提供者在發送過程中設置。優先級 0 的優先級最低,優先級 9 的優先級最高。

  • JMSCorrelationID——類型爲 string

    通常用來鏈接響應消息與請求消息,由發送消息的 JMS 程序設置。響應來自另一個 JMS 程序的消息的 JMS 程序將正響應消息的 JMSMessageID 拷貝到這個字段中,這樣,正作出響應的程序就可以與它所發出的特定請求的響應相 關聯

  • JMSReplyTo——類型爲 Destination

    請求程序用它來指出回覆消息應發送的地方,由發送消息的 JMS 程序設置。

  • JMSType——類型爲 string

    JMS 程序用它來指出消息的類型。一些提供者維護着一個消息類型倉庫,並用該字段引用倉庫中的定義類型,在這裏,JMS 程序不應該使用這個字段。

  • JMSRedelivered——類型爲 boolean

    指出消息被過早地發送給了 JMS 程序,程序不知道消息的接收者是誰;由提供者在接收過程中設置。

 

 

11、標準屬性

 

下面列表給出了 Message 的每一個標準屬性的名稱、它對應的 Java 類型和該屬性的說明。提供者對標準屬性的支持是可選的。JMS 爲這些屬性和將來 JMS 定義的屬性保留了 “JMSX” 屬性名。

  • JMSXUserID——類型爲 string

    發送消息的用戶的身份。

  • JMSXApplID——類型爲 string

    發送消息的應用程序的身份。

  • JMSXDeliveryCount——類型爲 int

    已經嘗試發送消息的次數。

  • JMSXGroupID——類型爲 string

    該消息所屬的消息組的身份。

  • JMSXGroupSeq——類型爲 int

    該消息在消息組中的序號。

  • JMSXProducerTXID——類型爲 string

    生成該消息的事務的身份。

  • JMSXConsumerTXID——類型爲 string

    使用該消息的事務的身份。

  • JMSXRcvTimestamp——類型爲 long

    JMS 將消息發送給客戶的時間。

  • JMSXState——類型爲 int

    提供者用它來維護消息的消息倉庫,通常,它與 JMS 生產者和客戶關係不大。

  • JMSX_<vendor_name>

    爲特定於提供者的屬性而保留。



《六》





1、product發送JMS消息

 

Java代碼  收藏代碼
  1. public class QueueSend {  
  2.     public static void main(String[] args) throws JMSException {  
  3.         ConnectionFactory connectionfactory = new ActiveMQConnectionFactory("tcp://localhost:61616");  
  4.         //創建與JMS服務的連接:ConnectionFactory被管理的對象,由客戶端創建,用來創建一個連接對象  
  5.         Connection connection = connectionfactory.createConnection();//獲取連接,connection一個到JMS系統提供者的活動連接  
  6.         Session session =connection.createSession(false,Session.AUTO_ACKNOWLEDGE );//打開會話,一個單獨的發送和接受消息的線程上下文  
  7.         QueueSend qs = new QueueSend();  
  8.           
  9.         qs.sendTextMsg(session,"使用jms發送文本消息","queue.msgText");//發送文本類型的消息  
  10.   
  11.         MapMessage mapMsg = session.createMapMessage();  
  12.         mapMsg.setString("name""李尋歡1");  
  13.         mapMsg.setBoolean("IsHero"true);  
  14.         mapMsg.setInt("age"35);  
  15.         qs.sendMap(session, mapMsg, "queue.msgMap");//發送map類型的消息  
  16.           
  17.         Person person = new Person("阿飛"23"北京.大興");//發送Object類型消息  
  18.         qs.sendObj(session, person, "queue.msgObj");  
  19.           
  20.         session.close();  
  21.         connection.close();  
  22.     }  
  23.     /* 
  24.      * 發送文本消息 
  25.      */  
  26.     public void sendTextMsg(Session session,String MsgContent,String name) throws JMSException{  
  27.         Queue queue = new ActiveMQQueue(name);  
  28.         MessageProducer msgProducer = session.createProducer(queue);  
  29.         Message msg = session.createTextMessage(MsgContent);  
  30.         msgProducer.send(msg);  
  31.         System.out.println("文本消息已發送");  
  32.     }  
  33.     /* 
  34.      * 發送MAP類型消息 
  35.      */  
  36.     public void sendMap(Session session,MapMessage map,String name) throws JMSException{  
  37.         Queue queue = new ActiveMQQueue(name);  
  38.         MessageProducer msgProducer = session.createProducer(queue);  
  39.         msgProducer.send(map);  
  40.         System.out.println("Map格式的消息已發送");  
  41.     }  
  42.     /* 
  43.      * 發送Object類型消息 
  44.      */  
  45.     public void sendObj(Session session,Object obj,String name) throws JMSException{  
  46.         Destination  queue = new ActiveMQQueue(name);//分裝消息的目的標示  
  47.         MessageProducer msgProducer = session.createProducer(queue);  
  48.         ObjectMessage objMsg=session.createObjectMessage((Serializable) obj);//發送對象時必須讓該對象實現serializable接口  
  49.         MessageProducer msgPorducer =session.createProducer(queue);  
  50.         msgPorducer.send(objMsg);  
  51.         System.out.println("Object類型的消息已發送");  
  52.           
  53.     }  
  54. }  
 當消息發送成功時我們可以再web消息管理端看到產生了如下圖所示的三條消息。


 2、consumer消費消息

 

接受jms消息代碼  收藏代碼
  1. public class QueueAccept implements MessageListener{  
  2.     public static void main(String[] args) throws JMSException {  
  3.         ConnectionFactory connectionfactory =null;  
  4.         Connection connection=null;  
  5.         Session session=null;  
  6.         if(connectionfactory==null){  
  7.             connectionfactory = new ActiveMQConnectionFactory("tcp://localhost:61616");  
  8.         }  
  9.         if(connection==null){  
  10.          connection = connectionfactory.createConnection();  
  11.             connection.start();  
  12.          }  
  13.          session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
  14.   
  15.         Queue queue = new ActiveMQQueue("queue.msgText");//根據發送的名稱接受消息  
  16.         MessageConsumer consumer = session.createConsumer(queue);  
  17.         consumer.setMessageListener(new QueueAccept());//不繼承MessageListener時可以用consumer.receive()手動接受消息  
  18.           
  19.         Queue queue1 = new ActiveMQQueue("queue.msgMap");  
  20.         MessageConsumer consumer1 = session.createConsumer(queue1);  
  21.         consumer1.setMessageListener(new QueueAccept());  
  22.           
  23.         Queue queue2 = new ActiveMQQueue("queue.msgObj");  
  24.         MessageConsumer consumer2 = session.createConsumer(queue2);  
  25.         consumer2.setMessageListener(new QueueAccept());  
  26.     }  
  27.       
  28.     public void onMessage(Message message) {  
  29.         /**  
  30.          * 接受文本類型的消息   
  31.          */  
  32.         if(message instanceof TextMessage){ //instanceof 測試它所指向的對象是否是TextMessage類  
  33.             TextMessage text = (TextMessage) message;  
  34.             try {  
  35.                 System.out.println("發送的文本消息內容爲:"+text.getText()); //接受文本消息   
  36.             } catch (JMSException e) {  
  37.                 e.printStackTrace();  
  38.             }  
  39.         }  
  40.         /**  
  41.          * 接受Map類型的消息  
  42.          */  
  43.         if(message instanceof MapMessage){  
  44.             MapMessage map = (MapMessage) message;  
  45.             try {  
  46.                 System.out.println("姓名:"+map.getString("name"));  
  47.                 System.out.println("是否是英雄:"+map.getBoolean("IsHero"));  
  48.                 System.out.println("年齡:"+map.getInt("age"));  
  49.             } catch (JMSException e) {  
  50.                 e.printStackTrace();  
  51.             }  
  52.         }  
  53.         if(message instanceof ObjectMessage){  
  54.             ObjectMessage objMsg = (ObjectMessage) message;  
  55.             try {  
  56.                 Person person=(Person) objMsg.getObject();  
  57.                 System.out.println("用戶名:"+person.getName()+"年齡:"+person.getAge()+"地址:"+person.getAddress());  
  58.             } catch (JMSException e) {  
  59.                 e.printStackTrace();  
  60.             }  
  61.         }  
  62.     }  
 客戶端運行後接受到的消息:


    在上邊的代碼中,我們在發送Object對象時,我們發送的對象需要實現serializable接口。記得剛找工作的時候有一回有個面試官問到,爲什麼一個類要實現serializable接口,也許許多人新人都不知道爲什麼,只能按照背的面試題中的答案去說。剛好這個demo中遇到了這個問題,就繼續複習一下java的基礎吧。一個類只有實現了serializable纔是可以序列化的,通俗的講實現了serializable接口後我們將可以把這個類,在網絡上進行發送,或者將這個類存入到硬盤,序列化的目的就是保存一個對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章