Spring整合JMS——基于ActiveMQ實現(一)

Spring整合JMS——基于ActiveMQ實現(一)

1.1???? JMS簡介

?????? JMS的全稱是Java Message Service,即Java消息服務。它主要用于在生產者和消費者之間進行消息傳遞,生產者負責產生消息,而消費者負責接收消息。把它應用到實際的業務需求中的話我們可以在特定的時候利用生產者生成一消息,并進行發送,對應的消費者在接收到對應的消息后去完成對應的業務邏輯。對于消息的傳遞有兩種類型,一種是點對點的,即一個生產者和一個消費者一一對應;另一種是發布/訂閱模式,即一個生產者產生消息并進行發送后,可以由多個消費者進行接收。

1.2???? Spring整合JMS

?????? 對JMS做了一個簡要介紹之后,接下來就講一下Spring整合JMS的具體過程。JMS只是一個標準,真正在使用它的時候我們需要有它的具體實現,這里我們就使用Apache的activeMQ來作為它的實現。所使用的依賴利用Maven來進行管理,具體依賴如下:

?

Xml代碼?復制代碼?收藏代碼
  1. <dependencies>??
  2. ????????<dependency>??
  3. ????????????<groupId>junit</groupId>??
  4. ????????????<artifactId>junit</artifactId>??
  5. ????????????<version>4.10</version>??
  6. ????????????<scope>test</scope>??
  7. ????????</dependency>??
  8. ????????<dependency>??
  9. ????????????<groupId>org.springframework</groupId>??
  10. ????????????<artifactId>spring-context</artifactId>??
  11. ????????????<version>${spring-version}</version>??
  12. ????????</dependency>??
  13. ????????<dependency>??
  14. ????????????<groupId>org.springframework</groupId>??
  15. ????????????<artifactId>spring-jms</artifactId>??
  16. ????????????<version>${spring-version}</version>??
  17. ????????</dependency>??
  18. ????????<dependency>??
  19. ????????????<groupId>org.springframework</groupId>??
  20. ????????????<artifactId>spring-test</artifactId>??
  21. ????????????<version>${spring-version}</version>??
  22. ????????</dependency>??
  23. ????????<dependency>??
  24. ????????????<groupId>javax.annotation</groupId>??
  25. ????????????<artifactId>jsr250-api</artifactId>??
  26. ????????????<version>1.0</version>??
  27. ????????</dependency>??
  28. ????????<dependency>??
  29. ????????????<groupId>org.apache.activemq</groupId>??
  30. ????????????<artifactId>activemq-core</artifactId>??
  31. ????????????<version>5.7.0</version>??
  32. ????????</dependency>??
  33. </dependencies>??

<dependencies>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.10</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>${spring-version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jms</artifactId>            <version>${spring-version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>${spring-version}</version>        </dependency>        <dependency>            <groupId>javax.annotation</groupId>            <artifactId>jsr250-api</artifactId>            <version>1.0</version>        </dependency>        <dependency>            <groupId>org.apache.activemq</groupId>            <artifactId>activemq-core</artifactId>            <version>5.7.0</version>        </dependency></dependencies>

?

1.2.1??activeMQ準備

?????? 既然是使用的apache的activeMQ作為JMS的實現,那么首先我們應該到apache官網上下載activeMQ(http://activemq.apache.org/download.html),進行解壓后運行其bin目錄下面的activemq.bat文件啟動activeMQ。

1.2.2配置ConnectionFactory

?????? ConnectionFactory是用于產生到JMS服務器的鏈接的,Spring為我們提供了多個ConnectionFactory,有SingleConnectionFactory和CachingConnectionFactory。SingleConnectionFactory對于建立JMS服務器鏈接的請求會一直返回同一個鏈接,并且會忽略Connection的close方法調用。CachingConnectionFactory繼承了SingleConnectionFactory,所以它擁有SingleConnectionFactory的所有功能,同時它還新增了緩存功能,它可以緩存Session、MessageProducer和MessageConsumer。這里我們使用SingleConnectionFactory來作為示例。

Xml代碼?復制代碼?收藏代碼
  1. <bean?id="connectionFactory"?class="org.springframework.jms.connection.SingleConnectionFactory"/>??

<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"/>

?

?????? 這樣就定義好產生JMS服務器鏈接的ConnectionFactory了嗎?答案是非也。Spring提供的ConnectionFactory只是Spring用于管理ConnectionFactory的,真正產生到JMS服務器鏈接的ConnectionFactory還得是由JMS服務廠商提供,并且需要把它注入到Spring提供的ConnectionFactory中。我們這里使用的是ActiveMQ實現的JMS,所以在我們這里真正的可以產生Connection的就應該是由ActiveMQ提供的ConnectionFactory。所以定義一個ConnectionFactory的完整代碼應該如下所示:

Xml代碼?復制代碼?收藏代碼
  1. <!--?真正可以產生Connection的ConnectionFactory,由對應的?JMS服務廠商提供-->??
  2. <bean?id="targetConnectionFactory"?class="org.apache.activemq.ActiveMQConnectionFactory">??
  3. ????<property?name="brokerURL"?value="tcp://localhost:61616"/>??
  4. </bean>??
  5. ??
  6. <!--?Spring用于管理真正的ConnectionFactory的ConnectionFactory?-->??
  7. <bean?id="connectionFactory"?class="org.springframework.jms.connection.SingleConnectionFactory">??
  8. ????<!--?目標ConnectionFactory對應真實的可以產生JMS?Connection的ConnectionFactory?-->??
  9. ????<property?name="targetConnectionFactory"?ref="targetConnectionFactory"/>??
  10. </bean>??

    <!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">        <property name="brokerURL" value="tcp://localhost:61616"/>    </bean>        <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">        <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>    </bean>

??

1.2.3配置生產者

配置好ConnectionFactory之后我們就需要配置生產者。生產者負責產生消息并發送到JMS服務器,這通常對應的是我們的一個業務邏輯服務實現類。但是我們的服務實現類是怎么進行消息的發送的呢?這通常是利用Spring為我們提供的JmsTemplate類來實現的,所以配置生產者其實最核心的就是配置進行消息發送的JmsTemplate。對于消息發送者而言,它在發送消息的時候要知道自己該往哪里發,為此,我們在定義JmsTemplate的時候需要往里面注入一個Spring提供的ConnectionFactory對象。

Xml代碼?復制代碼?收藏代碼
  1. <!--?Spring提供的JMS工具類,它可以進行消息發送、接收等?-->??
  2. <bean?id="jmsTemplate"?class="org.springframework.jms.core.JmsTemplate">??
  3. ????<!--?這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象?-->??
  4. ????<property?name="connectionFactory"?ref="connectionFactory"/>??
  5. </bean>??

    <!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 -->    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">        <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->        <property name="connectionFactory" ref="connectionFactory"/>    </bean>

?

?????? 在真正利用JmsTemplate進行消息發送的時候,我們需要知道消息發送的目的地,即destination。在Jms中有一個用來表示目的地的Destination接口,它里面沒有任何方法定義,只是用來做一個標識而已。當我們在使用JmsTemplate進行消息發送時沒有指定destination的時候將使用默認的Destination。默認Destination可以通過在定義jmsTemplate bean對象時通過屬性defaultDestination或defaultDestinationName來進行注入,defaultDestinationName對應的就是一個普通字符串。在ActiveMQ中實現了兩種類型的Destination,一個是點對點的ActiveMQQueue,另一個就是支持訂閱/發布模式的ActiveMQTopic。在定義這兩種類型的Destination時我們都可以通過一個name屬性來進行構造,如:

?

?

?

?

Xml代碼?復制代碼?收藏代碼
  1. <!--這個是隊列目的地,點對點的-->??
  2. <bean?id="queueDestination"?class="org.apache.activemq.command.ActiveMQQueue">??
  3. ????<constructor-arg>??
  4. ????????<value>queue</value>??
  5. ????</constructor-arg>??
  6. </bean>??
  7. <!--這個是主題目的地,一對多的-->??
  8. <bean?id="topicDestination"?class="org.apache.activemq.command.ActiveMQTopic">??
  9. ????<constructor-arg?value="topic"/>??
  10. </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>

?

?

?

?????? 假設我們定義了一個ProducerService,里面有一個向Destination發送純文本消息的方法sendMessage,那么我們的代碼就大概是這個樣子:

?

?

?

?

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.service.impl; ??
  2. ? ??
  3. import?javax.annotation.Resource; ??
  4. import?javax.jms.Destination; ??
  5. import?javax.jms.JMSException; ??
  6. import?javax.jms.Message; ??
  7. import?javax.jms.Session; ??
  8. ? ??
  9. import?org.springframework.jms.core.JmsTemplate; ??
  10. import?org.springframework.jms.core.MessageCreator; ??
  11. import?org.springframework.stereotype.Component; ??
  12. ? ??
  13. import?com.tiantian.springintejms.service.ProducerService; ??
  14. ? ??
  15. @Component??
  16. public?class?ProducerServiceImpl?implements?ProducerService?{ ??
  17. ? ??
  18. ????private?JmsTemplate?jmsTemplate; ??
  19. ???? ??
  20. ????public?void?sendMessage(Destination?destination,?final?String?message)?{ ??
  21. ????????System.out.println("---------------生產者發送消息-----------------"); ??
  22. ????????System.out.println("---------------生產者發了一個消息:"?+?message); ??
  23. ????????jmsTemplate.send(destination,?new?MessageCreator()?{ ??
  24. ????????????public?Message?createMessage(Session?session)?throws?JMSException?{ ??
  25. ????????????????return?session.createTextMessage(message); ??
  26. ????????????} ??
  27. ????????}); ??
  28. ????}? ??
  29. ??
  30. ????public?JmsTemplate?getJmsTemplate()?{ ??
  31. ????????returnjmsTemplate; ??
  32. ????}? ??
  33. ??
  34. ????@Resource??
  35. ????public?void?setJmsTemplate(JmsTemplate?jmsTemplate)?{ ??
  36. ????????this.jmsTemplate?=?jmsTemplate; ??
  37. ????} ??
  38. ? ??
  39. }??

package com.tiantian.springintejms.service.impl; import javax.annotation.Resource;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.Session; import org.springframework.jms.core.JmsTemplate;import org.springframework.jms.core.MessageCreator;import org.springframework.stereotype.Component; import com.tiantian.springintejms.service.ProducerService; @Componentpublic class ProducerServiceImpl implements ProducerService {     private JmsTemplate jmsTemplate;        public void sendMessage(Destination destination, final String message) {        System.out.println("---------------生產者發送消息-----------------");        System.out.println("---------------生產者發了一個消息:" + message);        jmsTemplate.send(destination, new MessageCreator() {            public Message createMessage(Session session) throws JMSException {                return session.createTextMessage(message);            }        });    }     public JmsTemplate getJmsTemplate() {        returnjmsTemplate;    }     @Resource    public void setJmsTemplate(JmsTemplate jmsTemplate) {        this.jmsTemplate = jmsTemplate;    } }

?

?

?

?????? 我們可以看到在sendMessage方法體里面我們是通過jmsTemplate來發送消息到對應的Destination的。到此,我們生成一個簡單的文本消息并把它發送到指定目的地Destination的生產者就配置好了。

1.2.4配置消費者

生產者往指定目的地Destination發送消息后,接下來就是消費者對指定目的地的消息進行消費了。那么消費者是如何知道有生產者發送消息到指定目的地Destination了呢?這是通過Spring為我們封裝的消息監聽容器MessageListenerContainer實現的,它負責接收信息,并把接收到的信息分發給真正的MessageListener進行處理。每個消費者對應每個目的地都需要有對應的MessageListenerContainer。對于消息監聽容器而言,除了要知道監聽哪個目的地之外,還需要知道到哪里去監聽,也就是說它還需要知道去監聽哪個JMS服務器,這是通過在配置MessageConnectionFactory的時候往里面注入一個ConnectionFactory來實現的。所以我們在配置一個MessageListenerContainer的時候有三個屬性必須指定,一個是表示從哪里監聽的ConnectionFactory;一個是表示監聽什么的Destination;一個是接收到消息以后進行消息處理的MessageListener。Spring一共為我們提供了兩種類型的MessageListenerContainer,SimpleMessageListenerContainer和DefaultMessageListenerContainer。

SimpleMessageListenerContainer會在一開始的時候就創建一個會話session和消費者Consumer,并且會使用標準的JMS MessageConsumer.setMessageListener()方法注冊監聽器讓JMS提供者調用監聽器的回調函數。它不會動態的適應運行時需要和參與外部的事務管理。兼容性方面,它非常接近于獨立的JMS規范,但一般不兼容Java EE的JMS限制。

大多數情況下我們還是使用的DefaultMessageListenerContainer,跟SimpleMessageListenerContainer相比,DefaultMessageListenerContainer會動態的適應運行時需要,并且能夠參與外部的事務管理。它很好的平衡了對JMS提供者要求低、先進功能如事務參與和兼容Java EE環境。

定義處理消息的MessageListener

?????? 要定義處理消息的MessageListener我們只需要實現JMS規范中的MessageListener接口就可以了。MessageListener接口中只有一個方法onMessage方法,當接收到消息的時候會自動調用該方法。

?

?

?

?

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.listener; ??
  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?ConsumerMessageListener?implements?MessageListener?{ ??
  9. ? ??
  10. ????public?void?onMessage(Message?message)?{ ??
  11. ????????//這里我們知道生產者發送的就是一個純文本消息,所以這里可以直接進行強制轉換,或者直接把onMessage方法的參數改成Message的子類TextMessage??
  12. ????????TextMessage?textMsg?=?(TextMessage)?message; ??
  13. ????????System.out.println("接收到一個純文本消息。"); ??
  14. ????????try?{ ??
  15. ????????????System.out.println("消息內容是:"?+?textMsg.getText()); ??
  16. ????????}?catch?(JMSException?e)?{ ??
  17. ????????????e.printStackTrace(); ??
  18. ????????} ??
  19. ????} ??
  20. ? ??
  21. }??

package com.tiantian.springintejms.listener; import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage; public class ConsumerMessageListener implements MessageListener {     public void onMessage(Message message) {        //這里我們知道生產者發送的就是一個純文本消息,所以這里可以直接進行強制轉換,或者直接把onMessage方法的參數改成Message的子類TextMessage        TextMessage textMsg = (TextMessage) message;        System.out.println("接收到一個純文本消息。");        try {            System.out.println("消息內容是:" + textMsg.getText());        } catch (JMSException e) {            e.printStackTrace();        }    } }

?

??

?

?????? 有了MessageListener之后我們就可以在Spring的配置文件中配置一個消息監聽容器了。

Xml代碼?復制代碼?收藏代碼
  1. <!--這個是隊列目的地-->??
  2. <bean?id="queueDestination"?class="org.apache.activemq.command.ActiveMQQueue">??
  3. ????<constructor-arg>??
  4. ????????<value>queue</value>??
  5. ????</constructor-arg>??
  6. </bean>??
  7. <!--?消息監聽器?-->??
  8. <bean?id="consumerMessageListener"?class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>???? ??
  9. ??
  10. <!--?消息監聽容器?-->??
  11. <bean?id="jmsContainer"????????class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  12. ????<property?name="connectionFactory"?ref="connectionFactory"?/>??
  13. ????<property?name="destination"?ref="queueDestination"?/>??
  14. ????<property?name="messageListener"?ref="consumerMessageListener"?/>??
  15. </bean>??

    <!--這個是隊列目的地-->    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>queue</value>        </constructor-arg>    </bean>    <!-- 消息監聽器 -->    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>        <!-- 消息監聽容器 -->    <bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory" />        <property name="destination" ref="queueDestination" />        <property name="messageListener" ref="consumerMessageListener" />    </bean>

?

?

?????? 我們可以看到我們定義了一個名叫queue的ActiveMQQueue目的地,我們的監聽器就是監聽了發送到這個目的地的消息。

?????? 至此我們的生成者和消費者都配置完成了,這也就意味著我們的整合已經完成了。這個時候完整的Spring的配置文件應該是這樣的:

Xml代碼?復制代碼?收藏代碼
  1. <?xml?version="1.0"?encoding="UTF-8"?>??
  2. <beans?xmlns="http://www.springframework.org/schema/beans"??
  3. ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?xmlns:context="http://www.springframework.org/schema/context"??
  4. ????xmlns:jms="http://www.springframework.org/schema/jms"??
  5. ????xsi:schemaLocation="http://www.springframework.org/schema/beans ??
  6. ?????http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
  7. ?????http://www.springframework.org/schema/context ??
  8. ?????http://www.springframework.org/schema/context/spring-context-3.0.xsd ??
  9. ????http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
  10. ????http://www.springframework.org/schema/jms?http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">??
  11. ? ??
  12. ????<context:component-scan?base-package="com.tiantian"?/>??
  13. ? ??
  14. ????<!--?Spring提供的JMS工具類,它可以進行消息發送、接收等?-->??
  15. ????<bean?id="jmsTemplate"?class="org.springframework.jms.core.JmsTemplate">??
  16. ????????<!--?這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象?-->??
  17. ????????<property?name="connectionFactory"?ref="connectionFactory"/>??
  18. ????</bean>??
  19. ???? ??
  20. ????<!--?真正可以產生Connection的ConnectionFactory,由對應的?JMS服務廠商提供-->??
  21. ????<bean?id="targetConnectionFactory"?class="org.apache.activemq.ActiveMQConnectionFactory">??
  22. ????????<property?name="brokerURL"?value="tcp://localhost:61616"/>??
  23. ????</bean>??
  24. ???? ??
  25. ????<!--?Spring用于管理真正的ConnectionFactory的ConnectionFactory?-->??
  26. ????<bean?id="connectionFactory"?class="org.springframework.jms.connection.SingleConnectionFactory">??
  27. ????????<!--?目標ConnectionFactory對應真實的可以產生JMS?Connection的ConnectionFactory?-->??
  28. ????????<property?name="targetConnectionFactory"?ref="targetConnectionFactory"/>??
  29. ????</bean>??
  30. ???? ??
  31. ????<!--這個是隊列目的地-->??
  32. ????<bean?id="queueDestination"?class="org.apache.activemq.command.ActiveMQQueue">??
  33. ????????<constructor-arg>??
  34. ????????????<value>queue</value>??
  35. ????????</constructor-arg>??
  36. ????</bean>??
  37. ????<!--?消息監聽器?-->??
  38. ????<bean?id="consumerMessageListener"?class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>??
  39. ????<!--?消息監聽容器?-->??
  40. ????<bean?id="jmsContainer"??
  41. ????????class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  42. ????????<property?name="connectionFactory"?ref="connectionFactory"?/>??
  43. ????????<property?name="destination"?ref="queueDestination"?/>??
  44. ????????<property?name="messageListener"?ref="consumerMessageListener"?/>??
  45. ????</bean>??
  46. </beans>??

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xmlns:jms="http://www.springframework.org/schema/jms"    xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     http://www.springframework.org/schema/context     http://www.springframework.org/schema/context/spring-context-3.0.xsd    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">     <context:component-scan base-package="com.tiantian" />     <!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 -->    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">        <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->        <property name="connectionFactory" ref="connectionFactory"/>    </bean>        <!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">        <property name="brokerURL" value="tcp://localhost:61616"/>    </bean>        <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">        <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>    </bean>        <!--這個是隊列目的地-->    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>queue</value>        </constructor-arg>    </bean>    <!-- 消息監聽器 -->    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>    <!-- 消息監聽容器 -->    <bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory" />        <property name="destination" ref="queueDestination" />        <property name="messageListener" ref="consumerMessageListener" />    </bean></beans>

?

?

?????? 接著我們來測試一下,看看我們的整合是否真的成功了,測試代碼如下:

?

?

?

?

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.test; ??
  2. ? ??
  3. import?javax.jms.Destination; ??
  4. ? ??
  5. import?org.junit.Test; ??
  6. import?org.junit.runner.RunWith; ??
  7. import?org.springframework.beans.factory.annotation.Autowired; ??
  8. import?org.springframework.beans.factory.annotation.Qualifier; ??
  9. import?org.springframework.test.context.ContextConfiguration; ??
  10. import?org.springframework.test.context.junit4.SpringJUnit4ClassRunner; ??
  11. import?com.tiantian.springintejms.service.ProducerService; ??
  12. ? ??
  13. @RunWith(SpringJUnit4ClassRunner.class) ??
  14. @ContextConfiguration("/applicationContext.xml") ??
  15. public?class?ProducerConsumerTest?{ ??
  16. ? ??
  17. ????@Autowired??
  18. ????private?ProducerService?producerService; ??
  19. ????@Autowired??
  20. ????@Qualifier("queueDestination") ??
  21. ????private?Destination?destination; ??
  22. ???? ??
  23. ????@Test??
  24. ????public?void?testSend()?{ ??
  25. ????????for?(int?i=0;?i<2;?i++)?{ ??
  26. ????????????producerService.sendMessage(destination,?"你好,生產者!這是消息:"?+?(i+1)); ??
  27. ????????} ??
  28. ????} ??
  29. ???? ??
  30. }??

package com.tiantian.springintejms.test; import javax.jms.Destination; import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.tiantian.springintejms.service.ProducerService; @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/applicationContext.xml")public class ProducerConsumerTest {     @Autowired    private ProducerService producerService;    @Autowired    @Qualifier("queueDestination")    private Destination destination;        @Test    public void testSend() {        for (int i=0; i<2; i++) {            producerService.sendMessage(destination, "你好,生產者!這是消息:" + (i+1));        }    }    }

?

?

?

?????? 在上面的測試代碼中我們利用生產者發送了兩個消息,正常來說,消費者應該可以接收到這兩個消息。運行測試代碼后控制臺輸出如下:

?

?

?????? 看,控制臺已經進行了正確的輸出,這說明我們的整合確實是已經成功了。

?

Spring整合JMS(二)——消息監聽器

?

3.1???? 消息監聽器MessageListener

?????? 在Spring整合JMS的應用中我們在定義消息監聽器的時候一共可以定義三種類型的消息監聽器,分別是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。下面就分別來介紹一下這幾種類型的區別。

3.1.1??MessageListener

MessageListener是最原始的消息監聽器,它是JMS規范中定義的一個接口。其中定義了一個用于處理接收到的消息的onMessage方法,該方法只接收一個Message參數。我們前面在講配置消費者的時候用的消息監聽器就是MessageListener,代碼如下:

?

Java代碼?復制代碼?收藏代碼
  1. import?javax.jms.JMSException; ??
  2. import?javax.jms.Message; ??
  3. import?javax.jms.MessageListener; ??
  4. import?javax.jms.TextMessage; ??
  5. ? ??
  6. public?class?ConsumerMessageListener?implements?MessageListener?{ ??
  7. ? ??
  8. ????public?void?onMessage(Message?message)?{ ??
  9. ????????//這里我們知道生產者發送的就是一個純文本消息,所以這里可以直接進行強制轉換,或者直接把onMessage方法的參數改成Message的子類TextMessage??
  10. ????????TextMessage?textMsg?=?(TextMessage)?message; ??
  11. ????????System.out.println("接收到一個純文本消息。"); ??
  12. ????????try?{ ??
  13. ????????????System.out.println("消息內容是:"?+?textMsg.getText()); ??
  14. ????????}?catch?(JMSException?e)?{ ??
  15. ????????????e.printStackTrace(); ??
  16. ????????} ??
  17. ????} ??
  18. ? ??
  19. }??
import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage; public class ConsumerMessageListener implements MessageListener {     public void onMessage(Message message) {        //這里我們知道生產者發送的就是一個純文本消息,所以這里可以直接進行強制轉換,或者直接把onMessage方法的參數改成Message的子類TextMessage        TextMessage textMsg = (TextMessage) message;        System.out.println("接收到一個純文本消息。");        try {            System.out.println("消息內容是:" + textMsg.getText());        } catch (JMSException e) {            e.printStackTrace();        }    } }

??

3.1.2??SessionAwareMessageListener

SessionAwareMessageListener是Spring為我們提供的,它不是標準的JMS MessageListener。MessageListener的設計只是純粹用來接收消息的,假如我們在使用MessageListener處理接收到的消息時我們需要發送一個消息通知對方我們已經收到這個消息了,那么這個時候我們就需要在代碼里面去重新獲取一個Connection或Session。SessionAwareMessageListener的設計就是為了方便我們在接收到消息后發送一個回復的消息,它同樣為我們提供了一個處理接收到的消息的onMessage方法,但是這個方法可以同時接收兩個參數,一個是表示當前接收到的消息Message,另一個就是可以用來發送消息的Session對象。先來看一段代碼:

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.listener; ??
  2. ? ??
  3. import?javax.jms.Destination; ??
  4. import?javax.jms.JMSException; ??
  5. import?javax.jms.Message; ??
  6. import?javax.jms.MessageProducer; ??
  7. import?javax.jms.Session; ??
  8. import?javax.jms.TextMessage; ??
  9. ? ??
  10. import?org.springframework.jms.listener.SessionAwareMessageListener; ??
  11. ? ??
  12. public?class?ConsumerSessionAwareMessageListener?implements??
  13. ????????SessionAwareMessageListener<TextMessage>?{ ??
  14. ? ??
  15. ????private?Destination?destination; ??
  16. ???? ??
  17. ????public?void?onMessage(TextMessage?message,?Session?session)?throws?JMSException?{ ??
  18. ????????System.out.println("收到一條消息"); ??
  19. ????????System.out.println("消息內容是:"?+?message.getText()); ??
  20. ????????MessageProducer?producer?=?session.createProducer(destination); ??
  21. ????????Message?textMessage?=?session.createTextMessage("ConsumerSessionAwareMessageListener。。。"); ??
  22. ????????producer.send(textMessage); ??
  23. ????} ??
  24. ? ??
  25. ????public?Destination?getDestination()?{ ??
  26. ????????returndestination; ??
  27. ????} ??
  28. ? ??
  29. ????public?void?setDestination(Destination?destination)?{ ??
  30. ????????this.destination?=?destination; ??
  31. ????} ??
  32. ? ??
  33. }??
package com.tiantian.springintejms.listener; import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageProducer;import javax.jms.Session;import javax.jms.TextMessage; import org.springframework.jms.listener.SessionAwareMessageListener; public class ConsumerSessionAwareMessageListener implements        SessionAwareMessageListener<TextMessage> {     private Destination destination;        public void onMessage(TextMessage message, Session session) throws JMSException {        System.out.println("收到一條消息");        System.out.println("消息內容是:" + message.getText());        MessageProducer producer = session.createProducer(destination);        Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener。。。");        producer.send(textMessage);    }     public Destination getDestination() {        returndestination;    }     public void setDestination(Destination destination) {        this.destination = destination;    } }

?

?????? 在上面代碼中我們定義了一個SessionAwareMessageListener,在這個Listener中我們在接收到了一個消息之后,利用對應的Session創建了一個到destination的生產者和對應的消息,然后利用創建好的生產者發送對應的消息。

?????? 接著我們在Spring的配置文件中配置該消息監聽器將處理來自一個叫sessionAwareQueue的目的地的消息,并且往該MessageListener中通過set方法注入其屬性destination的值為queueDestination。這樣當我們的SessionAwareMessageListener接收到消息之后就會往queueDestination發送一個消息。

Xml代碼?復制代碼?收藏代碼
  1. <?xml?version="1.0"?encoding="UTF-8"?>??
  2. <beans?xmlns="http://www.springframework.org/schema/beans"??
  3. ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?xmlns:context="http://www.springframework.org/schema/context"??
  4. ????xmlns:jms="http://www.springframework.org/schema/jms"??
  5. ????xsi:schemaLocation="http://www.springframework.org/schema/beans ??
  6. ?????http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
  7. ?????http://www.springframework.org/schema/context ??
  8. ?????http://www.springframework.org/schema/context/spring-context-3.0.xsd ??
  9. ????http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
  10. ????http://www.springframework.org/schema/jms?http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">??
  11. ? ??
  12. ????<context:component-scan?base-package="com.tiantian"?/>? ??
  13. ????<!--?Spring提供的JMS工具類,它可以進行消息發送、接收等?-->??
  14. ????<bean?id="jmsTemplate"?class="org.springframework.jms.core.JmsTemplate">??
  15. ????????<!--?這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象?-->??
  16. ????????<property?name="connectionFactory"?ref="connectionFactory"/>??
  17. ????</bean>??
  18. ???? ??
  19. ????<!--?真正可以產生Connection的ConnectionFactory,由對應的?JMS服務廠商提供-->??
  20. ????<bean?id="targetConnectionFactory"?class="org.apache.activemq.ActiveMQConnectionFactory">??
  21. ????????<property?name="brokerURL"?value="tcp://localhost:61616"/>??
  22. ????</bean>??
  23. ???? ??
  24. ????<!--?Spring用于管理真正的ConnectionFactory的ConnectionFactory?-->??
  25. ????<bean?id="connectionFactory"?class="org.springframework.jms.connection.SingleConnectionFactory">??
  26. ????????<!--?目標ConnectionFactory對應真實的可以產生JMS?Connection的ConnectionFactory?-->??
  27. ????????<property?name="targetConnectionFactory"?ref="targetConnectionFactory"/>??
  28. ????</bean>??
  29. ???? ??
  30. ????<!--這個是隊列目的地-->??
  31. ????<bean?id="queueDestination"?class="org.apache.activemq.command.ActiveMQQueue">??
  32. ????????<constructor-arg>??
  33. ????????????<value>queue</value>??
  34. ????????</constructor-arg>??
  35. ????</bean>??
  36. ????<!--這個是sessionAwareQueue目的地-->??
  37. ????<bean?id="sessionAwareQueue"?class="org.apache.activemq.command.ActiveMQQueue">??
  38. ????????<constructor-arg>??
  39. ????????????<value>sessionAwareQueue</value>??
  40. ????????</constructor-arg>??
  41. ????</bean>??
  42. ????<!--?消息監聽器?-->??
  43. ????<bean?id="consumerMessageListener"?class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>??
  44. ????<!--?可以獲取session的MessageListener?-->??
  45. ????<bean?id="consumerSessionAwareMessageListener"?class="com.tiantian.springintejms.listener.ConsumerSessionAwareMessageListener">??
  46. ????????<property?name="destination"?ref="queueDestination"/>??
  47. ????</bean>??
  48. ????<!--?消息監聽容器?-->??
  49. ????<bean?id="jmsContainer"????????class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  50. ????????<property?name="connectionFactory"?ref="connectionFactory"?/>??
  51. ????????<property?name="destination"?ref="queueDestination"?/>??
  52. ????????<property?name="messageListener"?ref="consumerMessageListener"?/>??
  53. ????</bean>??
  54. ???? ??
  55. ????<bean?id="sessionAwareListenerContainer"??
  56. ????????class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  57. ????????<property?name="connectionFactory"?ref="connectionFactory"?/>??
  58. ????????<property?name="destination"?ref="sessionAwareQueue"?/>??
  59. ????????<property?name="messageListener"?ref="consumerSessionAwareMessageListener"?/>??
  60. ????</bean>??
  61. </beans>??
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xmlns:jms="http://www.springframework.org/schema/jms"    xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     http://www.springframework.org/schema/context     http://www.springframework.org/schema/context/spring-context-3.0.xsd    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">     <context:component-scan base-package="com.tiantian" />     <!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 -->    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">        <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->        <property name="connectionFactory" ref="connectionFactory"/>    </bean>        <!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">        <property name="brokerURL" value="tcp://localhost:61616"/>    </bean>        <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">        <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>    </bean>        <!--這個是隊列目的地-->    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>queue</value>        </constructor-arg>    </bean>    <!--這個是sessionAwareQueue目的地-->    <bean id="sessionAwareQueue" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>sessionAwareQueue</value>        </constructor-arg>    </bean>    <!-- 消息監聽器 -->    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>    <!-- 可以獲取session的MessageListener -->    <bean id="consumerSessionAwareMessageListener" class="com.tiantian.springintejms.listener.ConsumerSessionAwareMessageListener">        <property name="destination" ref="queueDestination"/>    </bean>    <!-- 消息監聽容器 -->    <bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory" />        <property name="destination" ref="queueDestination" />        <property name="messageListener" ref="consumerMessageListener" />    </bean>        <bean id="sessionAwareListenerContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory" />        <property name="destination" ref="sessionAwareQueue" />        <property name="messageListener" ref="consumerSessionAwareMessageListener" />    </bean></beans>

?

?

?????? 接著我們來做一個測試,測試代碼如下:

Java代碼?復制代碼?收藏代碼
  1. @RunWith(SpringJUnit4ClassRunner.class) ??
  2. @ContextConfiguration("/applicationContext.xml") ??
  3. public?class?ProducerConsumerTest?{ ??
  4. ? ??
  5. ????@Autowired??
  6. ????private?ProducerService?producerService; ??
  7. ????@Autowired??
  8. ????@Qualifier("sessionAwareQueue") ??
  9. ????private?Destination?sessionAwareQueue; ??
  10. ???? ??
  11. ????@Test??
  12. ????public?void?testSessionAwareMessageListener()?{ ??
  13. ????????producerService.sendMessage(sessionAwareQueue,?"測試SessionAwareMessageListener"); ??
  14. ????} ??
  15. ???? ??
  16. }??
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/applicationContext.xml")public class ProducerConsumerTest {     @Autowired    private ProducerService producerService;    @Autowired    @Qualifier("sessionAwareQueue")    private Destination sessionAwareQueue;        @Test    public void testSessionAwareMessageListener() {        producerService.sendMessage(sessionAwareQueue, "測試SessionAwareMessageListener");    }    }

?

?????? 在上述測試代碼中,我們通過前面定義好的生產者往我們定義好的SessionAwareMessageListener監聽的sessionAwareQueue發送了一個消息。程序運行之后控制臺輸出如下:



?

?

?????? 這說明我們已經成功的往sessionAwareQueue發送了一條純文本消息,消息會被ConsumerSessionAwareMessageListener的onMessage方法進行處理,在onMessage方法中ConsumerSessionAwareMessageListener就是簡單的把接收到的純文本信息的內容打印出來了,之后再往queueDestination發送了一個純文本消息,消息內容是“ConsumerSessionAwareMessageListener…”,該消息隨后就被ConsumerMessageListener處理了,根據我們的定義,在ConsumerMessageListener中也只是簡單的打印了一下接收到的消息內容。

3.1.3??MessageListenerAdapter

MessageListenerAdapter類實現了MessageListener接口和SessionAwareMessageListener接口,它的主要作用是將接收到的消息進行類型轉換,然后通過反射的形式把它交給一個普通的Java類進行處理。

?????? MessageListenerAdapter會把接收到的消息做如下轉換:

?????? TextMessage轉換為String對象;

?????? BytesMessage轉換為byte數組;

?????? MapMessage轉換為Map對象;

?????? ObjectMessage轉換為對應的Serializable對象。

?????? 既然前面說了MessageListenerAdapter會把接收到的消息做一個類型轉換,然后利用反射把它交給真正的目標處理器——一個普通的Java類進行處理(如果真正的目標處理器是一個MessageListener或者是一個SessionAwareMessageListener,那么Spring將直接使用接收到的Message對象作為參數調用它們的onMessage方法,而不會再利用反射去進行調用),那么我們在定義一個MessageListenerAdapter的時候就需要為它指定這樣一個目標類。這個目標類我們可以通過MessageListenerAdapter的構造方法參數指定,如:

Xml代碼?復制代碼?收藏代碼
  1. <!--?消息監聽適配器?-->??
  2. ????<bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
  3. ????????<constructor-arg>??
  4. ????????????<bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
  5. ????????</constructor-arg>??
  6. ????</bean>??
<!-- 消息監聽適配器 -->    <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">        <constructor-arg>            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>        </constructor-arg>    </bean>

?

?

?????? 也可以通過它的delegate屬性來指定,如:

Xml代碼?復制代碼?收藏代碼
  1. <!--?消息監聽適配器?-->??
  2. ????<bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
  3. ????????<property?name="delegate">??
  4. ????????????<bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
  5. ????????</property>??
  6. ????????<property?name="defaultListenerMethod"?value="receiveMessage"/>??
  7. ????</bean>??
<!-- 消息監聽適配器 -->    <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"/>    </bean>

?

?

?????? 前面說了如果我們指定的這個目標處理器是一個MessageListener或者是一個SessionAwareMessageListener的時候Spring將直接利用接收到的Message對象作為方法參數調用它們的onMessage方法。但是如果指定的目標處理器是一個普通的Java類時Spring將利用Message進行了類型轉換之后的對象作為參數通過反射去調用真正的目標處理器的處理方法,那么Spring是如何知道該調用哪個方法呢?這是通過MessageListenerAdapter的defaultListenerMethod屬性來決定的,當我們沒有指定該屬性時,Spring會默認調用目標處理器的handleMessage方法。

?????? 接下來我們來看一個示例,假設我們有一個普通的Java類ConsumerListener,其對應有兩個方法,handleMessage和receiveMessage,其代碼如下:

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.listener; ??
  2. ? ??
  3. public?class?ConsumerListener?{ ??
  4. ? ??
  5. ????public?void?handleMessage(String?message)?{ ??
  6. ????????System.out.println("ConsumerListener通過handleMessage接收到一個純文本消息,消息內容是:"?+?message); ??
  7. ????} ??
  8. ???? ??
  9. ????public?void?receiveMessage(String?message)?{ ??
  10. ????????System.out.println("ConsumerListener通過receiveMessage接收到一個純文本消息,消息內容是:"?+?message); ??
  11. ????} ??
  12. ???? ??
  13. }??
package com.tiantian.springintejms.listener; 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);    }    }

?

?????? 假設我們要把它作為一個消息監聽器來監聽發送到adapterQueue的消息,這個時候我們就可以定義一個對應的MessageListenerAdapter來把它當做一個MessageListener使用。

Xml代碼?復制代碼?收藏代碼
  1. <!--?消息監聽適配器?-->??
  2. <bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
  3. ????<property?name="delegate">??
  4. ????????<bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
  5. ????</property>??
  6. ????<property?name="defaultListenerMethod"?value="receiveMessage"/>??
  7. </bean>??
    <!-- 消息監聽適配器 -->    <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"/>    </bean>

?

?????? 當然,有了MessageListener之后我們還需要配置其對應的MessageListenerContainer,這里配置如下:

Xml代碼?復制代碼?收藏代碼
  1. <!--?消息監聽適配器對應的監聽容器?-->??
  2. <bean?id="messageListenerAdapterContainer"?class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  3. ????<property?name="connectionFactory"?ref="connectionFactory"/>??
  4. ????<property?name="destination"?ref="adapterQueue"/>??
  5. ????<property?name="messageListener"?ref="messageListenerAdapter"/><!--?使用MessageListenerAdapter來作為消息監聽器?-->??
  6. </bean>??
    <!-- 消息監聽適配器對應的監聽容器 -->    <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory"/>        <property name="destination" ref="adapterQueue"/>        <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter來作為消息監聽器 -->    </bean>

?

?????? 在上面的MessageListenerAdapter中我們指定了其defaultListenerMethod屬性的值為receiveMessage,所以當MessageListenerAdapter接收到消息之后會自動的調用我們指定的ConsumerListener的receiveMessage方法。

?????? 針對于上述代碼我們定義測試代碼如下:

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.test; ??
  2. ? ??
  3. import?javax.jms.Destination; ??
  4. ? ??
  5. import?org.junit.Test; ??
  6. import?org.junit.runner.RunWith; ??
  7. import?org.springframework.beans.factory.annotation.Autowired; ??
  8. import?org.springframework.beans.factory.annotation.Qualifier; ??
  9. import?org.springframework.test.context.ContextConfiguration; ??
  10. import?org.springframework.test.context.junit4.SpringJUnit4ClassRunner; ??
  11. ? ??
  12. import?com.tiantian.springintejms.service.ProducerService; ??
  13. ? ??
  14. @RunWith(SpringJUnit4ClassRunner.class) ??
  15. @ContextConfiguration("/applicationContext.xml") ??
  16. public?class?ProducerConsumerTest?{ ??
  17. ??
  18. ????@Autowired??
  19. ????@Qualifier("adapterQueue") ??
  20. ????private?Destination?adapterQueue; ??
  21. ??
  22. ????@Test??
  23. ????public?void?testMessageListenerAdapter()?{ ??
  24. ????????producerService.sendMessage(adapterQueue,?"測試MessageListenerAdapter"); ??
  25. ????} ??
  26. ???? ??
  27. }??
package com.tiantian.springintejms.test; import javax.jms.Destination; import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.tiantian.springintejms.service.ProducerService; @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/applicationContext.xml")public class ProducerConsumerTest {    @Autowired    @Qualifier("adapterQueue")    private Destination adapterQueue;    @Test    public void testMessageListenerAdapter() {        producerService.sendMessage(adapterQueue, "測試MessageListenerAdapter");    }    }

?

?????? 這時候我們會看到控制臺輸出如下:



????????如果我們指定MessageListenerAdapter的defaultListenerMethod屬性,那么在運行上述代碼時控制臺會輸出如下結果:



????????MessageListenerAdapter除了會自動的把一個普通Java類當做MessageListener來處理接收到的消息之外,其另外一個主要的功能是可以自動的發送返回消息

???? 當我們用于處理接收到的消息的方法的返回值不為空的時候,Spring會自動將它封裝為一個JMS Message,然后自動進行回復。那么這個時候這個回復消息將發送到哪里呢?這主要有兩種方式可以指定。
???????第一,可以通過發送的Message的setJMSReplyTo方法指定該消息對應的回復消息的目的地。這里我們把我們的生產者發送消息的代碼做一下修改,在發送消息之前先指定該消息對應的回復目的地為一個叫responseQueue的隊列目的地,具體代碼如下所示:

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.service.impl; ??
  2. ? ??
  3. import?javax.jms.Destination; ??
  4. import?javax.jms.JMSException; ??
  5. import?javax.jms.Message; ??
  6. import?javax.jms.Session; ??
  7. import?javax.jms.TextMessage; ??
  8. ? ??
  9. import?org.springframework.beans.factory.annotation.Autowired; ??
  10. import?org.springframework.beans.factory.annotation.Qualifier; ??
  11. import?org.springframework.jms.core.JmsTemplate; ??
  12. import?org.springframework.jms.core.MessageCreator; ??
  13. import?org.springframework.stereotype.Component; ??
  14. ? ??
  15. import?com.tiantian.springintejms.service.ProducerService; ??
  16. ? ??
  17. @Component??
  18. public?class?ProducerServiceImpl?implements?ProducerService?{? ??
  19. ??
  20. ????@Autowired??
  21. ????private?JmsTemplate?jmsTemplate; ??
  22. ??
  23. ????@Autowired??
  24. ????@Qualifier("responseQueue") ??
  25. ????private?Destination?responseDestination; ??
  26. ???? ??
  27. ????public?void?sendMessage(Destination?destination,?final?String?message)?{ ??
  28. ????????System.out.println("---------------生產者發送消息-----------------"); ??
  29. ????????System.out.println("---------------生產者發了一個消息:"?+?message); ??
  30. ????????jmsTemplate.send(destination,?new?MessageCreator()?{ ??
  31. ????????????public?Message?createMessage(Session?session)?throws?JMSException?{ ??
  32. ????????????????TextMessage?textMessage?=?session.createTextMessage(message); ??
  33. ????????????????textMessage.setJMSReplyTo(responseDestination); ??
  34. ????????????????return?textMessage; ??
  35. ????????????} ??
  36. ????????}); ??
  37. ????} ??
  38. ? ??
  39. }??
package com.tiantian.springintejms.service.impl; import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.Session;import javax.jms.TextMessage; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.jms.core.JmsTemplate;import org.springframework.jms.core.MessageCreator;import org.springframework.stereotype.Component; import com.tiantian.springintejms.service.ProducerService; @Componentpublic class ProducerServiceImpl implements ProducerService {     @Autowired    private JmsTemplate jmsTemplate;    @Autowired    @Qualifier("responseQueue")    private Destination responseDestination;        public void sendMessage(Destination destination, final String message) {        System.out.println("---------------生產者發送消息-----------------");        System.out.println("---------------生產者發了一個消息:" + message);        jmsTemplate.send(destination, new MessageCreator() {            public Message createMessage(Session session) throws JMSException {                TextMessage textMessage = session.createTextMessage(message);                textMessage.setJMSReplyTo(responseDestination);                return textMessage;            }        });    } }

?

?????? 接著定義一個叫responseQueue的隊列目的地及其對應的消息監聽器和監聽容器。

Xml代碼?復制代碼?收藏代碼
  1. <!--?用于測試消息回復的?-->??
  2. <bean?id="responseQueue"?class="org.apache.activemq.command.ActiveMQQueue">??
  3. ????<constructor-arg>??
  4. ????????<value>responseQueue</value>??
  5. ????</constructor-arg>??
  6. </bean>??
  7. ??
  8. <!--?responseQueue對應的監聽器?-->??
  9. <bean?id="responseQueueListener"?class="com.tiantian.springintejms.listener.ResponseQueueListener"/>??
  10. ??
  11. <!--?responseQueue對應的監聽容器?-->??
  12. <bean?id="responseQueueMessageListenerContainer"?class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  13. ????<property?name="connectionFactory"?ref="connectionFactory"/>??
  14. ????<property?name="destination"?ref="responseQueue"/>??
  15. ????<property?name="messageListener"?ref="responseQueueListener"/>??
  16. </bean>??
    <!-- 用于測試消息回復的 -->    <bean id="responseQueue" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>responseQueue</value>        </constructor-arg>    </bean>    <!-- responseQueue對應的監聽器 -->    <bean id="responseQueueListener" class="com.tiantian.springintejms.listener.ResponseQueueListener"/>    <!-- responseQueue對應的監聽容器 -->    <bean id="responseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory"/>        <property name="destination" ref="responseQueue"/>        <property name="messageListener" ref="responseQueueListener"/>    </bean>

??

?????? ResponseQueueListener的定義如下所示:

Java代碼?復制代碼?收藏代碼
  1. public?class?ResponseQueueListener?implements?MessageListener?{ ??
  2. ? ??
  3. ????public?void?onMessage(Message?message)?{ ??
  4. ????????if?(message?instanceof?TextMessage)?{ ??
  5. ????????????TextMessage?textMessage?=?(TextMessage)?message; ??
  6. ????????????try?{ ??
  7. ????????????????System.out.println("接收到發送到responseQueue的一個文本消息,內容是:"?+?textMessage.getText()); ??
  8. ????????????}?catch?(JMSException?e)?{ ??
  9. ????????????????e.printStackTrace(); ??
  10. ????????????} ??
  11. ????????} ??
  12. ????} ??
  13. ? ??
  14. }??
public class ResponseQueueListener implements MessageListener {     public void onMessage(Message message) {        if (message instanceof TextMessage) {            TextMessage textMessage = (TextMessage) message;            try {                System.out.println("接收到發送到responseQueue的一個文本消息,內容是:" + textMessage.getText());            } catch (JMSException e) {                e.printStackTrace();            }        }    } }

?

?????? 接著我們運行我們的測試代碼,利用生產者往我們定義好的MessageListenerAdapter負責處理的adapterQueue目的地發送一個消息。測試代碼如下所示:

Java代碼?復制代碼?收藏代碼
  1. @RunWith(SpringJUnit4ClassRunner.class) ??
  2. @ContextConfiguration("/applicationContext.xml") ??
  3. public?class?ProducerConsumerTest?{ ??
  4. ? ??
  5. ????@Autowired??
  6. ????private?ProducerService?producerService; ??
  7. ??
  8. ????@Qualifier("adapterQueue") ??
  9. ????@Autowired??
  10. ????private?Destination?adapterQueue;??? ??
  11. ??
  12. ????@Test??
  13. ????public?void?testMessageListenerAdapter()?{ ??
  14. ????????producerService.sendMessage(adapterQueue,?"測試MessageListenerAdapter"); ??
  15. ????} ??
  16. ???? ??
  17. }??
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/applicationContext.xml")public class ProducerConsumerTest {     @Autowired    private ProducerService producerService;    @Qualifier("adapterQueue")    @Autowired    private Destination adapterQueue;       @Test    public void testMessageListenerAdapter() {        producerService.sendMessage(adapterQueue, "測試MessageListenerAdapter");    }    }

?

?????? 運行上述測試代碼之后,控制臺輸出如下:

?



????????這說明我們的生產者發送消息被MessageListenerAdapter處理之后,MessageListenerAdapter確實把監聽器的返回內容封裝成一個Message往原Message通過setJMSReplyTo方法指定的回復目的地發送了一個消息。對于MessageListenerAdapter對應的監聽器處理方法返回的是一個null值或者返回類型是void的情況,MessageListenerAdapter是不會自動進行消息的回復的,有興趣的網友可以自己測試一下。

?????? 第二,通過MessageListenerAdapter的defaultResponseDestination屬性來指定。這里我們也來做一個測試,首先維持生產者發送消息的代碼不變,即發送消息前不通過Message的setJMSReplyTo方法指定消息的回復目的地;接著我們在定義MessageListenerAdapter的時候通過其defaultResponseDestination屬性指定其默認的回復目的地是“defaultResponseQueue”,并定義defaultResponseQueue對應的消息監聽器和消息監聽容器。

Xml代碼?復制代碼?收藏代碼
  1. <!--?消息監聽適配器?-->??
  2. <bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
  3. ????<!--?<constructor-arg>??
  4. ????????<bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
  5. ????</constructor-arg>?-->??
  6. ????<property?name="delegate">??
  7. ????????<bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
  8. ????</property>??
  9. ????<property?name="defaultListenerMethod"?value="receiveMessage"/>??
  10. ????<property?name="defaultResponseDestination"?ref="defaultResponseQueue"/>??
  11. </bean>??
  12. ??
  13. <!--?消息監聽適配器對應的監聽容器?-->??
  14. <bean?id="messageListenerAdapterContainer"?class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  15. ????<property?name="connectionFactory"?ref="connectionFactory"/>??
  16. ????<property?name="destination"?ref="adapterQueue"/>??
  17. ????<property?name="messageListener"?ref="messageListenerAdapter"/><!--?使用MessageListenerAdapter來作為消息監聽器?-->??
  18. </bean>??
  19. ??
  20. !--?默認的消息回復隊列?-->??
  21. <bean?id="defaultResponseQueue"?class="org.apache.activemq.command.ActiveMQQueue">??
  22. ????<constructor-arg>??
  23. ????????<value>defaultResponseQueue</value>??
  24. ????</constructor-arg>??
  25. </bean>??
  26. ??
  27. <!--?defaultResponseQueue對應的監聽器?-->??
  28. <bean?id="defaultResponseQueueListener"?class="com.tiantian.springintejms.listener.DefaultResponseQueueListener"/>??
  29. ??
  30. <!--?defaultResponseQueue對應的監聽容器?-->??
  31. <bean?id="defaultResponseQueueMessageListenerContainer"?class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
  32. ????<property?name="connectionFactory"?ref="connectionFactory"/>??
  33. ????<property?name="destination"?ref="defaultResponseQueue"/>??
  34. ????<property?name="messageListener"?ref="defaultResponseQueueListener"/>??
  35. </bean>??
    <!-- 消息監聽適配器 -->    <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">        <!-- <constructor-arg>            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>        </constructor-arg> -->        <property name="delegate">            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>        </property>        <property name="defaultListenerMethod" value="receiveMessage"/>        <property name="defaultResponseDestination" ref="defaultResponseQueue"/>    </bean>    <!-- 消息監聽適配器對應的監聽容器 -->    <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory"/>        <property name="destination" ref="adapterQueue"/>        <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter來作為消息監聽器 -->    </bean><!-- 默認的消息回復隊列 -->    <bean id="defaultResponseQueue" class="org.apache.activemq.command.ActiveMQQueue">        <constructor-arg>            <value>defaultResponseQueue</value>        </constructor-arg>    </bean>    <!-- defaultResponseQueue對應的監聽器 -->    <bean id="defaultResponseQueueListener" class="com.tiantian.springintejms.listener.DefaultResponseQueueListener"/>    <!-- defaultResponseQueue對應的監聽容器 -->    <bean id="defaultResponseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">        <property name="connectionFactory" ref="connectionFactory"/>        <property name="destination" ref="defaultResponseQueue"/>        <property name="messageListener" ref="defaultResponseQueueListener"/>    </bean>

?

?????? DefaultResponseQueueListener的代碼如下所示:

Java代碼?復制代碼?收藏代碼
  1. package?com.tiantian.springintejms.listener; ??
  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?DefaultResponseQueueListener?implements?MessageListener?{ ??
  9. ? ??
  10. ????public?void?onMessage(Message?message)?{ ??
  11. ????????if?(message?instanceof?TextMessage)?{ ??
  12. ????????????TextMessage?textMessage?=?(TextMessage)?message; ??
  13. ????????????try?{ ??
  14. ????????????????System.out.println("DefaultResponseQueueListener接收到發送到defaultResponseQueue的一個文本消息,內容是:"?+?textMessage.getText()); ??
  15. ????????????}?catch?(JMSException?e)?{ ??
  16. ????????????????e.printStackTrace(); ??
  17. ????????????} ??
  18. ????????} ??
  19. ????} ??
  20. ? ??
  21. }??
package com.tiantian.springintejms.listener; import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage; public class DefaultResponseQueueListener implements MessageListener {     public void onMessage(Message message) {        if (message instanceof TextMessage) {            TextMessage textMessage = (TextMessage) message;            try {                System.out.println("DefaultResponseQueueListener接收到發送到defaultResponseQueue的一個文本消息,內容是:" + textMessage.getText());            } catch (JMSException e) {                e.printStackTrace();            }        }    } }

?

?????? 這時候運行如下測試代碼:

Java代碼?復制代碼?收藏代碼
  1. @Test??
  2. public?void?testMessageListenerAdapter()?{ ??
  3. ????producerService.sendMessage(adapterQueue,?"測試MessageListenerAdapter"); ??
  4. }??
    @Test    public void testMessageListenerAdapter() {        producerService.sendMessage(adapterQueue, "測試MessageListenerAdapter");    }

?

?????? 控制臺將輸出如下內容:

?



????????這說明MessageListenerAdapter會自動把真正的消息處理器返回的非空內容封裝成一個Message發送回復消息到通過defaultResponseDestination屬性指定的默認消息回復目的地。

?????? 既然我們可以通過兩種方式來指定MessageListenerAdapter自動發送回復消息的目的地,那么當我們兩種方式都指定了而且它們的目的地還不一樣的時候會怎么發送呢?是兩個都發還是只發其中的一個呢?關于這部分的測試我這里就不贅述了,有興趣的網友可以自己進行。這里我可以直接的告訴大家,當兩種方式都指定了消息的回復目的地的時候使用發送消息的setJMSReplyTo方法指定的目的地將具有較高的優先級,MessageListenerAdapter將只往該方法指定的消息回復目的地發送回復消息。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/387889.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387889.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387889.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

軟件本地化 pdf_軟件本地化與標準翻譯

軟件本地化 pdfSoftware has become such an essential part of our world that it’s impossible to imagine a life without it. There’s hardly a service or product around us that wasn’t created with software or that runs on software.軟件已成為我們世界的重要組成…

CentOS7+CDH5.14.0安裝全流程記錄,圖文詳解全程實測-8CDH5安裝和集群配置

Cloudera Manager Server和Agent都啟動以后&#xff0c;就可以進行CDH5的安裝配置了。 準備文件 從 http://archive.cloudera.com/cdh5/parcels/中下載CDH5.14.0的相關文件 把CDH5需要的安裝文件放到主節點上&#xff0c;新建目錄為/opt/cloudera/parcel-repo把我們之前下載的…

node.js安裝部署測試

&#xff08;一&#xff09;安裝配置&#xff1a; 1&#xff1a;從nodejs.org下載需要的版本 2&#xff1a;直接安裝&#xff0c;默認設置 &#xff0c;默認安裝在c:\program files\nodejs下。 3&#xff1a;更改npm安裝模塊的默認目錄 &#xff08;默認目錄在安裝目錄下的node…

數據庫不停機導數據方案_如何計算數據停機成本

數據庫不停機導數據方案In addition to wasted time and sleepless nights, data quality issues lead to compliance risks, lost revenue to the tune of several million dollars per year, and erosion of trust — but what does bad data really cost your company? I’…

luogu4159 迷路 (矩陣加速)

考慮如果只有距離為1的邊&#xff0c;那我用在時間i到達某個點的狀態數矩陣 乘上轉移矩陣&#xff08;就是邊的鄰接矩陣&#xff09;&#xff0c;就能得到i1時間的 然后又考慮到邊權只有1~9&#xff0c;那可以把邊拆成只有距離為1的 具體做法是一個點拆成9個然后串聯 1 #includ…

社群系統ThinkSNS+ V2.2-V2.3升級教程

WARNING本升級指南僅適用于 2.2 版本升級至 2.3 版本&#xff0c;如果你并非 2.2 版本&#xff0c;請查看其他升級指南&#xff0c;Plus 程序不允許跨版本升級&#xff01;#更新代碼預計耗時&#xff1a; 2 小時這是你自我操作的步驟&#xff0c;確認將你的 2.2 版本代碼升級到…

BZOJ4881 線段游戲(二分圖+樹狀數組/動態規劃+線段樹)

相當于將線段劃分成兩個集合使集合內線段不相交&#xff0c;并且可以發現線段相交等價于逆序對。也即要將原序列劃分成兩個單增序列。由dilworth定理&#xff0c;如果存在長度>3的單減子序列&#xff0c;無解&#xff0c;可以先判掉。 這個時候有兩種顯然的暴力。 將點集劃分…

activemq部署安裝

一、架構和技術介紹 1、簡介 ActiveMQ 是Apache出品&#xff0c;最流行的&#xff0c;能力強勁的開源消息總線。完全支持JMS1.1和J2EE 1.4規范的 JMS Provider實現 2、activemq的特性 1. 多種語言和協議編寫客戶端。語言: Java, C, C, C#, Ruby, Perl, Python, PHP。應用協議: …

python初學者_面向初學者的20種重要的Python技巧

python初學者Python is among the most widely used market programming languages in the world. This is because of a variety of driving factors:Python是世界上使用最廣泛的市場編程語言之一。 這是由于多種驅動因素&#xff1a; It’s simple to understand. 很容易理解…

主串與模式串的匹配

主串與模式串的匹配 &#xff08;1&#xff09;BF算法&#xff1a; BF算法比較簡單直觀&#xff0c;其匹配原理是主串S.ch[i]和模式串T.ch[j]比較&#xff0c;若相等&#xff0c;則i和j分別指示串中的下一個位置&#xff0c;繼續比較后續字符&#xff0c;若不相等&#xff0c;從…

什么是 DDoS 攻擊?

歡迎訪問網易云社區&#xff0c;了解更多網易技術產品運營經驗。 全稱Distributed Denial of Service&#xff0c;中文意思為“分布式拒絕服務”&#xff0c;就是利用大量合法的分布式服務器對目標發送請求&#xff0c;從而導致正常合法用戶無法獲得服務。通俗點講就是利用網絡…

nginx 并發過十萬

一般來說nginx 配置文件中對優化比較有作用的為以下幾項&#xff1a; worker_processes 8; nginx 進程數&#xff0c;建議按照cpu 數目來指定&#xff0c;一般為它的倍數。 worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000; 為每…

貝葉斯網絡建模

I am feeling sick. Fever. Cough. Stuffy nose. And it’s wintertime. Do I have the flu? Likely. Plus I have muscle pain. More likely.我感到惡心。 發熱。 咳嗽。 鼻塞。 現在是冬天。 我有流感嗎&#xff1f; 可能吧 另外我有肌肉疼痛。 更傾向于。 Bayesian networ…

長春南關區凈月大街附近都有哪些課后班?

長春南關區凈月大街附近都有哪些課后班&#xff1f;在學校的教育不能滿足廣大學生的需求的時候&#xff0c;一對一輔導、文化課輔導、高考輔導等越來越多的家長和孩子的選擇。相對于學校的大課教育&#xff0c;一對一輔導有著自身獨特的優勢&#xff0c;一對一輔導有著學校教學…

dev中文本框等獲取焦點事件

<ClientSideEvents GotFocus"GotFocus" /> editContract.SetFocus()//設置文本框等的焦點 function GotFocus(s, e) { window.top.DLG.show(700, 600, "PrePayment/ContractSelect.aspx", "選擇", null ); }…

數據科學家數據分析師_使您的分析師和數據科學家在數據處理方面保持一致

數據科學家數據分析師According to a recent survey conducted by Dimensional Research, only 50 percent of data analysts’ time is actually spent analyzing data. What’s the other half spent on? Data cleanup — that tedious and repetitive work that must be do…

神經網絡使用情景

神經網絡使用情景 人臉&#xff0f;圖像識別語音搜索文本到語音&#xff08;轉錄&#xff09;垃圾郵件篩選&#xff08;異常情況探測&#xff09;欺詐探測推薦系統&#xff08;客戶關系管理、廣告技術、避免用戶流失&#xff09;回歸分析 為何選擇Deeplearning4j&#xff1f; …

BZOJ4890 Tjoi2017城市

顯然刪掉的邊肯定是直徑上的邊。考慮枚舉刪哪一條。然后考慮怎么連。顯然新邊應該滿足其兩端點在各自樹中作為根能使樹深度最小。只要線性求出這個東西就可以了&#xff0c;這與求樹的重心的過程類似。 #include<iostream> #include<cstdio> #include<cmath>…

【國際專場】laravel多用戶平臺(SaaS, 如淘寶多用戶商城)的搭建策略

想不想用Laravel來搭建一個多用戶、或多租戶平臺&#xff1f;比如像淘寶那樣的多商戶平臺呢&#xff1f;聽上去很復雜&#xff0c;不是嗎&#xff1f;怎么能一個程序&#xff0c;給那么多的機構用戶來用呢&#xff1f;如何協調管理它們呢&#xff1f;數據庫怎么搭建呢&#xff…

GitHub常用命令及使用

GitHub使用介紹 摘要&#xff1a; 常用命令&#xff1a; git init 新建一個空的倉庫git status 查看狀態git add . 添加文件git commit -m 注釋 提交添加的文件并備注說明git remote add origin gitgithub.com:jinzhaogit/git.git 連接遠程倉庫git push -u origin master 將本地…