使用gradle搭建Spring+ActiveMQ的demo步驟

使用 gradle 搭建 spring + ActiveMQ 的一個 demo

參考:https://juejin.im/post/5ad46f34518825651d08265c#heading-15
https://www.imooc.com/learn/856

第一個是掘金的一篇文章,第二個是慕課網上的一個視頻課程。


以下是使用 gradle 搭建 spring + ActiveMQ 的一個 demo 的步驟

1.下載 ActiveMQ

首先去 ActiveMQ 官網下載 ActiveMQ 軟件,官網地址:https://activemq.apache.org/

2.引入依賴

創建一個新的 ActiveMQ 項目,並在 build.gradle 中引入相關的依賴

// https://mvnrepository.com/artifact/org.springframework/spring-context
compile group: 'org.springframework', name: 'spring-context', version: '5.1.9.RELEASE'
// https://mvnrepository.com/artifact/org.apache.activemq/activemq-all
compile group: 'org.apache.activemq', name: 'activemq-all', version: '5.15.9'
// https://mvnrepository.com/artifact/org.apache.activemq/activemq-pool
compile group: 'org.apache.activemq', name: 'activemq-pool', version: '5.15.9'
// https://mvnrepository.com/artifact/org.springframework/spring-jms
compile group: 'org.springframework', name: 'spring-jms', version: '5.1.9.RELEASE'

3.開始寫代碼

3.1 隊列模式

3.1.1 創建消息消費者

爲了觀察隊列模式的特點,這裏我創建了兩個隊列模式的消費者,其實是自定義兩個消息監聽器,讓其實現 MessageListener 接口,重寫 onMessage() 方法。代碼如下:

package com.caihao.activemqdemo.consumer;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
 * 隊列模式-消息消費者1
 */
public class QueueListener1 implements MessageListener{

  @Override
  public void onMessage(Message message) {
    // 由於我這裏只負責接收TextMessage類型,因此做一個類型判斷
    if (message instanceof TextMessage){
      TextMessage textMessage = (TextMessage) message;
      try {
        System.out.println("隊列模式消費者1:"+textMessage.getText());
      } catch (JMSException e) {
        e.printStackTrace();
      }
    }
  }
}

由於這兩個消息監聽器類代碼類似,因此就不放出另一個類了。

3.1.2 配置spring和activeMQ

在項目的 resources 資源環境目錄下新建一個 spring 的 xml 配置文件。

<?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"
  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">
  <!--生產者和消費者都需要用到的-start-->
  <!--配置包掃描路徑-->
  <context:component-scan base-package="com.caihao.activemqdemo.producer"/>
  <context:component-scan base-package="com.caihao.activemqdemo.consumer"/>

  <!--ActiveMQ爲我們提供的ConnectionFactory-->
  <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616"/>
  </bean>
  <!--spring jms 爲我們提供的連接池,這個connectionFactory也是下面的jmsTemplate要使用的,
  它其實就是對activeMQ的ConnectionFactory的一個封裝-->
  <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
  </bean>

  <!--提供一個隊列模式的目的地,點對點的-->
  <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
    <!--目的地隊列的名字-->
    <constructor-arg value="queue-demo"/>
  </bean>
  <!--生產者和消費者都需要用到的-end-->

  <!--生產者所需要的-start-->
  <!--配置jmsTemplate用於發送消息-->
  <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory"/>
  </bean>
  <!--生產者所需要的-end-->

  <!--消費者所需要的start-->
  <!--配置消息監聽器1,即消息的消費者-->
  <bean id="queueListener1" class="com.caihao.activemqdemo.consumer.QueueListener1"/>
  <!--配置消息容器1-->
  <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="queueDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="queueListener1"/>
  </bean>
  <!--配置消息監聽器2,即消息的消費者-->
  <bean id="queueListener2" class="com.caihao.activemqdemo.consumer.QueueListener2"/>
  <!--配置消息容器2-->
  <bean id="jmsContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="queueDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="queueListener2"/>
  </bean>
  <!--消費者所需要的end-->

</beans>

具體的解釋已經寫在代碼中了,這裏說一下,由於我是打算用兩個隊列模式的消息監聽器,因此,在配置文件中我需要配置兩個消息監聽器和消息容器。其次就是,如果我們需要把生產者和消費者分開配置的話,則只需要按照我所註釋的生產者/消費者/公共的/start-end 進行劃分即可。

3.1.3 創建消息生產者

這裏,我們新建一個類叫 Producer,提供一個方法,在方法裏面通過 jmsTemplate 進行發送消息。其中裏面的 queueDestination 爲上面的 xml 配置文件中的 queueDestination,這裏我們不能使用 Autowired 註解,因爲他們的類型不一樣,我們需要通過 Resources 註解的 name 屬性來進行指定。

package com.caihao.activemqdemo.producer;

import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

/**
 * 消息生產者
 */
@Component
public class Producer {

  @Autowired
  private JmsTemplate jmsTemplate;
  @Resource(name = "queueDestination")
  private Destination queueDestination;

  /**
   * 發送隊列消息
   */
  public void sendQueueMessage(String message) {
    // 發送消息
    jmsTemplate.send(queueDestination, new MessageCreator() {
      @Override
      public Message createMessage(Session session) throws JMSException {
        // 返回創建的消息
        return session.createTextMessage(message);
      }
    });
  }
}
3.1.4 運行測試代碼

生產者、消費者和配置文件都寫好之後就可以開始寫測試代碼了。這裏我們定義一個普通 java 類來運行代碼。在 main 方法中首先進行配置文件的加載,然後獲取生產者實例,接着調用生產者實例發送消息。

package com.caihao.activemqdemo;

import com.caihao.activemqdemo.producer.Producer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {

  public static void main(String[] args) {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-activemq.xml");
    // 獲取生產者實例
    Producer producer = (Producer) ac.getBean("producer");
    for (int i = 0; i < 100; i++) {
      // 發送隊列消息
      producer.sendQueueMessage("hello" + i);
    }
    // ac.close();
  }
}

在運行測試代碼之前,還需要先將之前下載的 ActiveMQ 啓動起來。找到 ActiveMQ 解壓後的 bin 目錄,然後根據操作系統選擇 64 還是 32 位,然後運行 win64/win32 中的 activemq.bat 。當在啓動的窗口日誌中看到類似 http://0.0.0.0:8161/的時候,說明 activemq 啓動起來了。這個時候,打開瀏覽器訪問 http://127.0.0.1:8161/即可打開activemq的管理界面。然後點擊 Manager ActiveMQ broker會彈出一個框讓輸入用戶名和密碼,activemq 的用戶名和密碼都是 admin 。之後運行測試代碼,就能在 activemq 的管理界面的導航欄中 Queues 一欄中看到消息隊列的相關信息了。控制檯也會打印出消費者所消費的信息,觀察兩個消費者所消費消息的規律,可以看到,它們基本上是一個消費者消費一條消息。一條消息被一個消費者消費了就不能再被另一個消費者所消費。另外,如果只啓動了消息的生產者進行發送消息,而消費者一個都沒啓動的話,消息生產者發送的消息會被存在消息隊列中,不會丟失,等到後面啓動了消費者之後,消費者還能自動去消息隊列裏面進行消費消息。

3.2 topic模式

廣播模式步驟和上面的隊列模式步驟類似。

3.2.1 創建消息消費者

在上面項目的基礎上,我再創建兩個消息的消費者,這次的消息消費者是 Topic 模式消費者,除了類名和隊列模式消費者不一樣,內容基本一致。代碼如下。

package com.caihao.activemqdemo.consumer;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

/**
 * topic模式消費者1
 */
public class TopicListener1 implements MessageListener {

  @Override
  public void onMessage(Message message) {
    if (message instanceof TextMessage) {
      TextMessage textMessage = (TextMessage) message;
      try {
        System.out.println("topic模式消費者1:" + textMessage.getText());
      } catch (JMSException e) {
        e.printStackTrace();
      }
    }
  }
}

第二個消費者類就不貼出了。

3.2.2 配置 spring 和 activemq

這裏我還是在之前新建的 spring-activemq.xml 中進行追加配置。首先配置 topic 模式的目的地。由於這個東西需要在生產者和消費者中都有用到,因此我將其放在生產者/消費者公共部分的 start/end 裏面。

<!--提供一個topic模式的目的地,廣播的-->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
	<!--topic模式名稱-->
	<constructor-arg value="topic-demo"/>
</bean>

然後接着配置消息監聽器,這一部分,配置在消費者的 start/end 裏面就可以了。

<!--topic模式監聽器-->
<!--配置消息監聽器1,即消息的消費者-->
<bean id="topicListener1" class="com.caihao.activemqdemo.consumer.TopicListener1"/>
<!--配置消息容器1-->
<bean id="topicContainer1" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="topicDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="topicListener1"/>
</bean>
<!--配置消息監聽器2,即消息的消費者-->
<bean id="topicListener2" class="com.caihao.activemqdemo.consumer.TopicListener2"/>
<!--配置消息容器1-->
<bean id="topicContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="topicDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="topicListener2"/>
</bean>

這樣配置就完成了。爲了看的明白,我給出 spring-activemq.xml 的全部內容,並將 topic 模式追加的配置進行突出顯示。

<?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"
  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">
  <!--生產者和消費者都需要用到的-start-->
  <!--配置包掃描路徑-->
  <context:component-scan base-package="com.caihao.activemqdemo.producer"/>
  <context:component-scan base-package="com.caihao.activemqdemo.consumer"/>

  <!--ActiveMQ爲我們提供的ConnectionFactory-->
  <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616"/>
  </bean>
  <!--spring jms 爲我們提供的連接池,這個connectionFactory也是下面的jmsTemplate要使用的,
  它其實就是對activeMQ的ConnectionFactory的一個封裝-->
  <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
  </bean>

  <!--提供一個隊列模式的目的地,點對點的-->
  <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
    <!--目的地隊列的名字-->
    <constructor-arg value="queue-demo"/>
  </bean>

  <!--提供一個topic模式的目的地,廣播的-->
  <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
    <!--topic模式名稱-->
    <constructor-arg value="topic-demo"/>
  </bean>
  <!--生產者和消費者都需要用到的-end-->

  <!--生產者所需要的-start-->
  <!--配置jmsTemplate用於發送消息-->
  <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory"/>
  </bean>
  <!--生產者所需要的-end-->

  <!--消費者所需要的start-->

  <!--配置消息監聽器1,即消息的消費者-->
  <bean id="queueListener1" class="com.caihao.activemqdemo.consumer.QueueListener1"/>
  <!--配置消息容器1-->
  <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="queueDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="queueListener1"/>
  </bean>
  <!--配置消息監聽器2,即消息的消費者-->
  <bean id="queueListener2" class="com.caihao.activemqdemo.consumer.QueueListener2"/>
  <!--配置消息容器2-->
  <bean id="jmsContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="queueDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="queueListener2"/>
  </bean>

  <!--topic模式監聽器-->
  <!--配置消息監聽器1,即消息的消費者-->
  <bean id="topicListener1" class="com.caihao.activemqdemo.consumer.TopicListener1"/>
  <!--配置消息容器1-->
  <bean id="topicContainer1" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="topicDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="topicListener1"/>
  </bean>
  <!--配置消息監聽器2,即消息的消費者-->
  <bean id="topicListener2" class="com.caihao.activemqdemo.consumer.TopicListener2"/>
  <!--配置消息容器1-->
  <bean id="topicContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <!--指定連接工廠-->
    <property name="connectionFactory" ref="connectionFactory"/>
    <!--指定消息目的地-->
    <property name="destination" ref="topicDestination"/>
    <!--指定消息監聽器-->
    <property name="messageListener" ref="topicListener2"/>
  </bean>
  <!--消費者所需要的end-->

</beans>

3.2.3 創建消息生產者

這裏還是在之前創建的 Producer 的類中追加一個方法發送 topic 模式的消息的方法。內容與 queue 隊列模式的差不多,首先引入一個 spring-activemq.xml 文件中定義的 topicDestination,然後通過 jmsTemplate 發送消息(注:這裏創建消息這裏,我使用了lambda表達式,實際內容其實和之前的創建消息代碼是一樣的)。

@Resource(name = "topicDestination")
private Destination topicDestination;

/**
 * 發送topic模式消息
 *
 * @param message 消息
 */
public void sendTopicMessage(String message) {
  // 發送消息
  jmsTemplate.send(topicDestination, (session) -> session.createTextMessage(message));
}

3.2.4 運行測試代碼

這裏,直接修改之前運行隊列模式時的 main 方法,在發送消息的時候選擇調用sendTopicMessage()方法即可。在運行測試代碼之前不要忘了啓動 activemq 。通過運行測試代碼發現,兩個topic消費者都收到了同樣的消息,也就是一個消息被這兩個消費者同時消費了,就類似於廣播,每個監聽了該消息隊列的監聽者都能收到消息。在 activemq 的管理控制檯的 topic 界面上也能看到,Messages Enqueued 有100個,而 Message Dequeued 有200個。

topic 模式的特點就是類似於廣播,只要是對這個消息隊列有監聽的人都能收到消息。但是如果是先運行了消息生產者發佈消息,而沒有提前運行消費者監聽消息的話,那麼等消息消費者之後啓動監聽的話,是無法消費之前生產者發送的消息的。其實這也很好理解,就和廣播一樣,如果你沒有提前開啓廣播,那麼廣播之前的東西你是收聽不到的。

方法,在發送消息的時候選擇調用 sendTopicMessage() 方法即可。在運行測試代碼之前不要忘了啓動 activemq 。通過運行測試代碼發現,兩個topic 消費者都收到了同樣的消息,也就是一個消息被這兩個消費者同時消費了,就類似於廣播,每個監聽了該消息隊列的監聽者都能收到消息。在 activemq 的管理控制檯的 topic 界面上也能看到,Messages Enqueued 有 100 個,而Message Dequeued 有 200 個。

topic 模式的特點就是類似於廣播,只要是對這個消息隊列有監聽的人都能收到消息。但是如果是先運行了消息生產者發佈消息,而沒有提前運行消費者監聽消息的話,那麼等消息消費者之後啓動監聽的話,是無法消費之前生產者發送的消息的。其實這也很好理解,就和廣播一樣,如果你沒有提前開啓廣播,那麼廣播之前的東西你是收聽不到的。

代碼:https://github.com/caiworld/activemq

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