隨筆 - 6  文章 - 129  trackbacks - 0
          <2008年1月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿(14)

          隨筆檔案(6)

          文章分類(lèi)(467)

          文章檔案(423)

          相冊(cè)

          收藏夾(18)

          JAVA

          搜索

          •  

          積分與排名

          • 積分 - 827233
          • 排名 - 49

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          異步進(jìn)程通信是面向服務(wù)架構(gòu)(SOA)一個(gè)重要的組成部分,因?yàn)槠髽I(yè)里很多系統(tǒng)通信,特別是與外部組織間的通信,實(shí)質(zhì)上都是異步的。Java消息服務(wù)(JMS)是用于編寫(xiě)使用異步消息傳遞的JEE應(yīng)用程序的API。傳統(tǒng)的使用JMS API進(jìn)行消息傳遞的實(shí)現(xiàn)包括多個(gè)步驟,例如JNDI查詢(xún)隊(duì)列連接工廠和Queue資源,在實(shí)際發(fā)送和接收消息前創(chuàng)建一個(gè)JMS會(huì)話。

             Spring框架則簡(jiǎn)化了使用JEE組件(包括JMS)的任務(wù)。它提供的模板機(jī)制隱藏了典型的JMS實(shí)現(xiàn)的細(xì)節(jié),這樣開(kāi)發(fā)人員可以集中精力放在處理消息的實(shí)際工作中,而不用擔(dān)心如何去創(chuàng)建,訪問(wèn)或清除JMS資源。

             本文將對(duì)Spring JMS API作一個(gè)概述,并通過(guò)一個(gè)運(yùn)行在JBoss MQ服務(wù)器上的web例程來(lái)介紹如何使用Spring JMS API來(lái)異步處理(發(fā)送和接收)消息。我將通過(guò)傳統(tǒng)JMS實(shí)現(xiàn)和Spring JMS實(shí)現(xiàn)兩者間的比較,來(lái)展示使用Spring JMS處理消息是如何的簡(jiǎn)單和靈活。

          異步消息傳遞和面向服務(wù)架構(gòu)

            在現(xiàn)實(shí)中,大多數(shù)web請(qǐng)求都是同步處理的。例如,當(dāng)用戶(hù)要登入一個(gè)網(wǎng)站,首先輸入用戶(hù)名和密碼,然后服務(wù)器驗(yàn)證登錄合法性。如果驗(yàn)證成功,程序?qū)⒃试S該用戶(hù)進(jìn)入網(wǎng)站。這里,登錄請(qǐng)求在從客戶(hù)端接收以后被即時(shí)處理了。信用卡驗(yàn)證是另一個(gè)同步處理的例子;只有服務(wù)器證實(shí)輸入的信用卡號(hào)是有效的,同時(shí)客戶(hù)在帳戶(hù)上有足夠的存款,客戶(hù)才被允許繼續(xù)操作。但是讓我們思考一下在順序處理系統(tǒng)上的支付結(jié)算步驟。一旦系統(tǒng)證實(shí)該用戶(hù)信用卡的信息是準(zhǔn)確的,并且在帳戶(hù)上有足夠的資金,就不必等到所有的支付細(xì)節(jié)落實(shí)、轉(zhuǎn)賬完成。支付結(jié)算可以異步方式進(jìn)行,這樣客戶(hù)可以繼續(xù)進(jìn)行核查操作。

             需要比典型同步請(qǐng)求耗費(fèi)更長(zhǎng)時(shí)間的請(qǐng)求,可以使用異步處理。另一個(gè)異步處理的例子是,在本地貸款處理程序中,提交至自動(dòng)承銷(xiāo)系統(tǒng)(AUS)的信用請(qǐng)求處理過(guò)程。當(dāng)借方提交貸款申請(qǐng)后,抵押公司會(huì)向AUS發(fā)送請(qǐng)求,以獲取信用歷史記錄。由于這個(gè)請(qǐng)求要求得到全面而又詳細(xì)的信用報(bào)告,包括借方現(xiàn)今和過(guò)去的帳戶(hù),最近的付款和其他財(cái)務(wù)資料,服務(wù)器需要耗費(fèi)較長(zhǎng)的時(shí)間(幾小時(shí)或著有時(shí)甚至是幾天)來(lái)對(duì)這些請(qǐng)求作出響應(yīng)。客戶(hù)端程序(應(yīng)用)要與服務(wù)器連接并耗費(fèi)如此長(zhǎng)的時(shí)間來(lái)等待結(jié)果,這是毫無(wú)意義的。因此通信應(yīng)該是異步發(fā)生的;也就是,一旦請(qǐng)求被提交,它就被放置在隊(duì)列中,同時(shí)客戶(hù)端與服務(wù)器斷開(kāi)連接。然后AUS服務(wù)從指定的隊(duì)列中選出請(qǐng)求進(jìn)行處理,并將處理得到的消息放置在另一個(gè)消息隊(duì)列里。最后,客戶(hù)端程序從這個(gè)隊(duì)列中選出處理結(jié)果,緊接著處理這個(gè)信用歷史數(shù)據(jù)。

          JMS

             如果您使用過(guò)JMS代碼,您會(huì)發(fā)現(xiàn)它與JDBC或JCA很像。它所包含的樣本代碼創(chuàng)建或JMS資源對(duì)象回溯,使得每一次您需要寫(xiě)一個(gè)新類(lèi)來(lái)發(fā)送和接收消息時(shí),都具有更好的代碼密集性和重復(fù)性。以下序列顯示了傳統(tǒng)JMS實(shí)現(xiàn)所包括的步驟:

          1. 創(chuàng)建JNDI初始上下文(context)。
          2. 從JNDI上下文獲取一個(gè)隊(duì)列連接工廠。
          3. 從隊(duì)列連接工廠中獲取一個(gè)Quene。
          4. 創(chuàng)建一個(gè)Session對(duì)象。
          5. 創(chuàng)建一個(gè)發(fā)送者(sender)或接收者(receiver)對(duì)象。
          6. 使用步驟5創(chuàng)建的發(fā)送者或接收者對(duì)象發(fā)送或接收消息。
          7. 處理完消息后,關(guān)閉所有JMS資源。

          您可以看到,步驟6是處理消息的唯一地方。其他步驟都只是管理與實(shí)際業(yè)務(wù)要求無(wú)關(guān)的JMS資源,但是開(kāi)發(fā)人員必須編寫(xiě)并維護(hù)這些額外步驟的代碼。

          Spring JMS

             Spring框架提供了一個(gè)模板機(jī)制來(lái)隱藏Java APIs的細(xì)節(jié)。JEE開(kāi)發(fā)人員可以使用JDBCTemplate和JNDITemplate類(lèi)來(lái)分別訪問(wèn)后臺(tái)數(shù)據(jù)庫(kù)和JEE資源(數(shù)據(jù)源,連接池)。JMS也不例外。Spring提供JMSTemplate類(lèi),因此開(kāi)發(fā)人員不用為一個(gè)JMS實(shí)現(xiàn)去編寫(xiě)樣本代碼。接下來(lái)是在開(kāi)發(fā)JMS應(yīng)用程序時(shí)Spring所具有一些的優(yōu)勢(shì)。

          1. 提供JMS抽象API,簡(jiǎn)化了訪問(wèn)目標(biāo)(隊(duì)列或主題)和向指定目標(biāo)發(fā)布消息時(shí)JMS的使用。
          2. JEE開(kāi)發(fā)人員不需要關(guān)心JMS不同版本(例如JMS 1.0.2與JMS 1.1)之間的差異。
          3. 開(kāi)發(fā)人員不必專(zhuān)門(mén)處理JMS異常,因?yàn)镾pring為所有JMS異常提供了一個(gè)未經(jīng)檢查的異常,并在JMS代碼中重新拋出。

          示例程序

                  說(shuō)明:因?yàn)橹皇菫榱搜菔救绾问褂胹pring編寫(xiě)jms的應(yīng)用,所以本例沒(méi)有什么實(shí)際用途。

                  程序功能:MessageProducer.java根據(jù)一用戶(hù)信息產(chǎn)生一個(gè)消息發(fā)送到 JMS Provider;由MessageConsumer.java接收。

          1.在Jboss里配置XML文件創(chuàng)建一個(gè)新的JMS provider。
          打開(kāi)位于%JBOSS_HOME%server\default\deploy\jms文件夾下的jbossmq-destinations-service.xml文件,加入以下代碼片斷:
           <!--  Register User Send/Receive Queue  -->
           <mbean code="org.jboss.mq.server.jmx.Queue"
             name="jboss.mq.destination:service=Queue,name=registerUserQueue">
             <depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
           </mbean>
           <!--  Register User Send/Receive Topic  -->
           <mbean code="org.jboss.mq.server.jmx.Topic"
            name="jboss.mq.destination:service=Topic,name=registerUserTopic">
             <depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
           </mbean>
          2.在spring的配置文件中配置JMS組件的具體細(xì)節(jié)。
           (1)JNDI上下文是取得JMS資源的起始位置,因此首先我們要配置JNDI模板:
              <!-- JNDI上下文(它是取得JMS資源的起始位置) -->
             <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
              <property name="environment">
               <props>
                <prop key="java.naming.factory.initial">
                 org.jnp.interfaces.NamingContextFactory
                </prop>
                <prop key="java.naming.provider.url">localhost</prop>
                <prop key="java.naming.factory.url.pkgs">
                 org.jnp.interfaces:org.jboss.naming
                </prop>
               </props>
              </property>
             </bean>
             注意:此JNDI模板用到了org.jnp.interfaces.NamingContextFactory所以要把%JBOSS_HOME%\client下的jbossall-client.jar加到你的項(xiàng)目的classpath中。
          (2)配置連接工廠:
             <!-- JMS連接工廠 -->
               <bean id="jmsConnectionFactory"class="org.springframework.jndi.JndiObjectFactoryBean">
              <property name="jndiTemplate">
               <ref bean="jndiTemplate" />
              </property>
              <property name="jndiName">
               <value>XAConnectionFactory</value>
              </property>
             </bean>
             注意:XAConnectionFactory這個(gè)JNDI名字是在%JBOSS_HOME%server\default\deploy\jms文件夾下的jms-ds.xml中定義的(它是由JBoss指定的)。
           (3)配置JmsTemplate組件。在例程中我們使用JmsTemplate102。同時(shí)使用defaultDestination屬性來(lái)指定JMS目標(biāo)。
            <!-- JMS模板配置 -->
            <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate102">
             <property name="connectionFactory" ref="jmsConnectionFactory" />
             <property name="defaultDestination" ref="destination" />
             <property name="pubSubDomain">
              <value>true</value>
             </property>
             <!-- 等待消息的時(shí)間(ms) -->
             <property name="receiveTimeout">
                   <value>30000</value>
                </property>
            </bean>
            注意:如果使用topic-subscribe(主題訂閱)模式,該模板的pubSubDomain屬性值為true;若使用PToP(點(diǎn)對(duì)點(diǎn))模式,pubSubDomain屬性值為false或不配置該屬性。
           (4)定義一個(gè)JMS目標(biāo)來(lái)發(fā)送和接收消息:
            <bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
             <property name="jndiTemplate">
              <ref bean="jndiTemplate" />
             </property>
             <property name="jndiName">
              <value>topic/registerUserTopic</value>
             </property>
            </bean>
           (5)配置發(fā)送者和接收者組件:
            <!-- 消息發(fā)布者 -->
            <bean id="msgProducer" class="com.boco.jms.MessageProducer">
             <property name="jmsTemplate" ref="jmsTemplate" />
            </bean>
            <!-- 消息接收者 -->
            <bean id="msgConsumer" class="com.boco.jms.MessageConsumer">
             <property name="jmsTemplate" ref="jmsTemplate" />
            </bean>
          3.相應(yīng)的類(lèi):
           (1). User對(duì)象。
             /**
             *  User.java
             *  created on Jul 2, 2006
             *  Copyrights 2006 BOCO,Inc. All rights reserved.
             */
            package com.boco.dto;
            
            import java.io.Serializable;
            
            /**
             * desc: 用戶(hù)信息 Bean
             * @author qiujy
             */
            public class User {
             private int id;
             private String username;
             private String password;
             private String email;
             
             public User(){}
             
             //以下為Getter,setter方法略
             ......
            }
            
           (2).消息生產(chǎn)者:
             /**
             *  MessageProducer.java
             *  created on Jul 22, 2006
             *  Copyrights 2006 BOCO,Inc. All rights reserved.
             */
            package com.boco.jms;
            
            import javax.jms.JMSException;
            import javax.jms.MapMessage;
            import javax.jms.Message;
            import javax.jms.Session;
            
            import org.springframework.jms.core.JmsTemplate;
            import org.springframework.jms.core.MessageCreator;
            
            import com.boco.dto.User;
            
            /**
             * desc:消息生產(chǎn)者
             * @author qiujy
             *
             */
            public class MessageProducer {
             /** JMS模板 */
             private JmsTemplate jmsTemplate;
             
             public void setJmsTemplate(JmsTemplate jmsTemplate){
              this.jmsTemplate = jmsTemplate;
             }
             
             public void sendMessage(final User user){
              //調(diào)用模板的send來(lái)發(fā)送消息
              jmsTemplate.send(new MessageCreator(){
            
               public Message createMessage(Session session) throws JMSException {
                //構(gòu)造一個(gè)要發(fā)送的消息
                MapMessage message = session.createMapMessage();
                 message.setInt("id", user.getId());
                 message.setString("username", user.getUsername());
                 message.setString("password", user.getPassword());
                 message.setString("email", user.getEmail());
                System.out.println("send success!!");
                return message;
               }
              });
             }
            }
            
           (3).消息消費(fèi)者:
            /**
             *  MessageConsumer.java
             *  created on Jul 22, 2006
             *  Copyrights 2006 BOCO,Inc. All rights reserved.
             */
            package com.boco.jms;
            
            import javax.jms.JMSException;
            import javax.jms.MapMessage;
            
            import org.springframework.jms.core.JmsTemplate;
            
            import com.boco.dto.User;
            
            /**
             * desc:消息消費(fèi)者
             * @author qiujy
             *
             */
            public class MessageConsumer {
             /** JMS模板 */
             private JmsTemplate jmsTemplate;
             
             public void setJmsTemplate(JmsTemplate jmsTemplate){
              this.jmsTemplate = jmsTemplate;
             }
             
             public User receiveMessage(){
              //參數(shù)為Destination的JNDI名字去掉前面的模式類(lèi)型標(biāo)識(shí)
              //MapMessage msg = (MapMessage)jmsTemplate.receive("registerUserQueue");
              MapMessage msg = (MapMessage)jmsTemplate.receive("registerUserTopic");
              User user = new User();
              
              try {
               user.setId(msg.getInt("id"));
               user.setUsername(msg.getString("username"));
               user.setPassword(msg.getString("password"));
               user.setEmail(msg.getString("email"));
              } catch (JMSException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
              }
              
              return user;
             }
            }

           (4).測(cè)試用例:
             //======== 生產(chǎn)者測(cè)試用例 ===============
             /**
             *  TestMsgProducer.java
             *  created on Jul 22, 2006
             *  Copyrights 2006 BOCO,Inc. All rights reserved.
             */
            package com.boco.jms;
            
            import junit.framework.TestCase;
            
            import org.springframework.context.ApplicationContext;
            import org.springframework.context.support.ClassPathXmlApplicationContext;
            
            import com.boco.dto.User;
            
            /**
             * desc:
             * @author qiujy
             *
             */
            public class TestMsgProducer extends TestCase {
            
             private ApplicationContext context;
             /**
              * @param arg0
              */
             public TestMsgProducer(String arg0) {
              super(arg0);
              context = new ClassPathXmlApplicationContext("applicationContext_jms.xml");
             }
            
             /* (non-Javadoc)
              * @see junit.framework.TestCase#setUp()
              */
             protected void setUp() throws Exception {
              super.setUp();
             }
            
             /* (non-Javadoc)
              * @see junit.framework.TestCase#tearDown()
              */
             protected void tearDown() throws Exception {
              super.tearDown();
             }
            
             /**
              * Test method for {@link com.boco.jms.MessageProducer#sendMessage(com.boco.dto.User)}.
              */
             public void testSendMessage() {
              User user = new User();
              user.setId(132);
              user.setUsername("JMSTest");
              user.setPassword("password");
              user.setEmail("support@boco.com.cn");
              
              MessageProducer producer = (MessageProducer)context.getBean("msgProducer");
              
              producer.sendMessage(user);
              
             }
            
            }

            //============ 消費(fèi)者測(cè)試用例 ===============
            /**
             *  TestMsgConsumer.java
             *  created on Jul 22, 2006
             *  Copyrights 2006 BOCO,Inc. All rights reserved.
             */
            package com.boco.jms;
            
            import junit.framework.TestCase;
            
            import org.springframework.context.ApplicationContext;
            import org.springframework.context.support.ClassPathXmlApplicationContext;
            
            import com.boco.dto.User;
            
            /**
             * desc:
             * @author qiujy
             *
             */
            public class TestMsgConsumer extends TestCase {
             private ApplicationContext context;
             /**
              * @param arg0
              */
             public TestMsgConsumer(String arg0) {
              super(arg0);
              context = new ClassPathXmlApplicationContext("applicationContext_jms.xml");
             }
            
             /* (non-Javadoc)
              * @see junit.framework.TestCase#setUp()
              */
             protected void setUp() throws Exception {
              super.setUp();
             }
            
             /* (non-Javadoc)
              * @see junit.framework.TestCase#tearDown()
              */
             protected void tearDown() throws Exception {
              super.tearDown();
             }
            
             /**
              * Test method for {@link com.boco.jms.MessageConsumer#receiveMessage()}.
              */
             public void testReceiveMessage() {
              MessageConsumer consumer = (MessageConsumer)context.getBean("msgConsumer");
              User user = consumer.receiveMessage();
              assertNotNull(user);
              System.out.println( "id========" + user.getId()
                  + "\nname======" + user.getUsername()
                  + "\npassword==" + user.getPassword()
                  + "\nemail=====" + user.getEmail());
             }
            
            }

           


          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1418938

          啟動(dòng)JBOSS服務(wù)器.先運(yùn)行TestMsgConsumer.java;再運(yùn)行TestMsgProducer.java,即可看到控制臺(tái)的輸出了,嘻嘻~~~~~



          posted on 2008-01-15 21:00 Ke 閱讀(2810) 評(píng)論(2)  編輯  收藏 所屬分類(lèi): springjbossjms

          FeedBack:
          # re: 轉(zhuǎn):用Spring快速開(kāi)發(fā)jms應(yīng)用(JBOSS服務(wù)器) 2008-01-15 21:02 KE
          講得好了.很詳細(xì)啊.郁悶了幾天,終于運(yùn)行成功了我的第一個(gè)JMS例子.很開(kāi)心.得多謝文章的主人  回復(fù)  更多評(píng)論
            
          # re: 轉(zhuǎn):用Spring快速開(kāi)發(fā)jms應(yīng)用(JBOSS服務(wù)器) 2009-10-23 10:33 benson
          想請(qǐng)教一下...這個(gè)能與WEB 結(jié)合嗎??配置連接工廠 的檔案位置是?配置JmsTemplate組件檔案位置是??  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 宁南县| 水富县| 宜城市| 湖口县| 五原县| 浏阳市| 睢宁县| 绿春县| 新竹县| 清水河县| 微博| 涞水县| 阳泉市| 汤原县| 上杭县| 鱼台县| 江门市| 南丹县| 广宁县| 南城县| 长海县| 井冈山市| 兖州市| 灵石县| 林芝县| 张家界市| 阜宁县| 陆良县| 林甸县| 射洪县| 黄山市| 大姚县| 泰宁县| 平利县| 同江市| 青岛市| 邵东县| 三穗县| 阿瓦提县| 山阴县| 丹阳市|