Spring+ActiveMQ整合測試
本例用於展示Spring+ActiveMQ的簡單使用,例子實現瞭如下幾個功能:
(1)、基於java配置ActiveMQ;
(2)、使用ActiveMQ傳送queue訊息;
(3)、使用ActiveMQ傳送topic訊息;
(4)、消費ActiveMQ的queue訊息;
(5)、消費ActiveMQ的topic訊息;
本文只是實現Spring+ActiveMQ的簡單整合使用,對於Spring+ActiveMQ講解比較詳細的部落格可以參考如下文章:
http://elim.iteye.com/blog/1893038
http://blog.csdn.net/lifetragedy/article/details/51836557
1、ActiveMQ安裝
首先從如下地址下載ActiveMQ的安裝檔案:
http://activemq.apache.org/download.html
下載對應的版本後解壓壓縮檔案(本文使用windows 64位的開發環境)。進入對應的解壓目錄(如本文中的路徑為apache-activemq-5.9.0\bin\win64)下執行如下指令碼啟動ActiveMQ:
activemq.bat
啟動成功後可以使用如下連結來訪問ActiveMQ的控制後臺,使用預設密碼登入即可(預設密碼在解壓目錄的conf目錄下的jetty-realm.properties檔案中可以檢視到,如本文的apache-activemq-5.9.0\conf\jetty-realm.properties檔案)。
http://localhost:8161/admin
注意:5.14版本的mq需要用jdk1.7以上,否則會報錯起不來。報錯資訊如下:
Unsupported major.minor version 51.0
2、依賴jar包
主要需要匯入如下兩個jar包。
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
3、java配置原始碼
本文中的配置使用java類+註解的方式配置,相關的配置原始碼如下:
package cn.hi_fei.ActiveMQ.configuration;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageListener;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import cn.hi_fei.ActiveMQ.listener.ConsumerMessageListener;
@Configuration
@ComponentScan(value = { "cn.hi_fei.ActiveMQ.service.impl" })
public class RootConfiguration {
@Bean("targetConnectionFactory")
public ConnectionFactory getTargetConnectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
factory.setBrokerURL("tcp://localhost:61616");
return factory;
}
/**
* Get spring MQ connection factory.
*
* @param mqFactory
* @return
*/
@Bean("springConnectionFactory")
public ConnectionFactory getSpringConnectionFactory(
ConnectionFactory targetConnectionFactory) {
SingleConnectionFactory factory = new SingleConnectionFactory();
factory.setTargetConnectionFactory(targetConnectionFactory);
return factory;
}
/**
* Get JMS template by spring connection factory.
* @param springConnectionFactory
* @return
*/
@Bean
public JmsTemplate getJmsTemplate(ConnectionFactory springConnectionFactory) {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(springConnectionFactory);
return template;
}
/**
* Create a queue destination by active MQ.
* @return
*/
@Bean("queueDestination")
public Destination getQueueDestination() {
ActiveMQQueue queue = new ActiveMQQueue("queue");
return queue;
}
/**
* Create a queue destination by active MQ.
* @return
*/
@Bean("topicDestination")
public Destination getTopicDestination() {
ActiveMQTopic topic = new ActiveMQTopic("topic");
return topic;
}
@Bean
@Profile("customer")
public MessageListener getConsumerMessageListener() {
ConsumerMessageListener listener = new ConsumerMessageListener();
return listener;
}
@Bean
@Profile("customer")
public DefaultMessageListenerContainer getQueueMessageListenerContainer(
ConnectionFactory springConnectionFactory,
Destination queueDestination,
MessageListener consumerMessageListener) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(springConnectionFactory);
container.setDestination(queueDestination);
container.setMessageListener(consumerMessageListener);
return container;
}
@Bean
@Profile("customer")
public DefaultMessageListenerContainer getTopicMessageListenerContainer(
ConnectionFactory springConnectionFactory,
Destination topicDestination,
MessageListener consumerMessageListener) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(springConnectionFactory);
container.setDestination(topicDestination);
container.setMessageListener(consumerMessageListener);
return container;
}
}
4、訊息生產者實現
訊息生產者用於傳送訊息到目的地。本文中在介面IProducerService中進行定義,定義的原始碼如下:
package cn.hi_fei.ActiveMQ.service;
import javax.jms.Destination;
public interface IProducerService {
/**
* 傳送普通的純文字訊息
* @param destination
* @param message
*/
public void sendMessage(final Destination destination,final String message);
}
介面的實現原始碼如下:
package cn.hi_fei.ActiveMQ.service.impl;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
import cn.hi_fei.ActiveMQ.service.IProducerService;
@Component
public class ProducerServiceImpl implements IProducerService{
private Logger logger = Logger.getLogger(ProducerServiceImpl.class);
@Autowired
private JmsTemplate jmsTemplate;
public void sendMessage(Destination destination, final String message) {
logger.info("---------------send message by producer:" + message);
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
}
}
5、訊息監聽器
訊息監聽器用於監聽訊息,通常用於監聽、讀取訊息並通知消費者進行消費。本文中的訊息監聽器實現原始碼如下:
package cn.hi_fei.ActiveMQ.listener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.apache.log4j.Logger;
public class ConsumerMessageListener implements MessageListener{
private Logger logger = Logger.getLogger(ConsumerMessageListener.class);
public void onMessage(Message arg0) {
if(arg0 instanceof TextMessage) {
TextMessage tMsg = (TextMessage)arg0;
try {
logger.info(tMsg.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
6、測試類
6.1、基類
老習慣,首先建立一個測試基類(BaseTester類)用於統一載入配置,具體的測試實現在其子類中。基類的原始碼如下:
package cn.hi_fei.ActiveMQ;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.hi_fei.ActiveMQ.configuration.RootConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={RootConfiguration.class})
public class BaseTester {
}
6.2、生產者測試類
生產者測試類繼承與測試基類,實現傳送訊息到queue和topic。在測試類中僅啟用了“producer”的profile,所以不會建立訊息監聽容器。原始碼如下:
package cn.hi_fei.ActiveMQ.impl;
import javax.jms.Destination;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ActiveProfiles;
import cn.hi_fei.ActiveMQ.BaseTester;
import cn.hi_fei.ActiveMQ.service.IProducerService;
@ActiveProfiles("producer")
public class ProducerTester extends BaseTester{
private Logger logger = Logger.getLogger(ProducerTester.class);
@Autowired
private IProducerService mProducer;
@Autowired
@Qualifier("queueDestination")
private Destination mQueueDestination;
@Autowired
@Qualifier("topicDestination")
private Destination mTopicDestination;
@Test
public void sendMessage() {
logger.info("Send message to queue...");
mProducer.sendMessage(mQueueDestination, "123123123");
logger.info("Send message to queue...");
mProducer.sendMessage(mTopicDestination, "456789");
logger.info("Active producer end...");
}
}
執行此測試用例,結果如下:
圖1、單獨執行生產者傳送訊息到queue和topic
此時開啟ActiveMQ的控制檯可以在queue和topic中即看到傳送的訊息個數,但是本例中還沒有相關消費者對訊息進行消費。
6.3、消費者測試類
消費者測試類也繼承於測試基類,但是啟用了“customer”的profile,此時訊息監聽器的容器建立,應用進行訊息的消費。相關原始碼如下:
package cn.hi_fei.ActiveMQ.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.test.context.ActiveProfiles;
import cn.hi_fei.ActiveMQ.BaseTester;
@ActiveProfiles("customer")
public class CustomerTester extends BaseTester{
private Logger logger = Logger.getLogger(CustomerTester.class);
@Test
public void waitToRead() {
try {
logger.info("Wait to customer queue message.");
InputStreamReader is_reader = new InputStreamReader(System.in);
new BufferedReader(is_reader).readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
原始碼比較粗糙。啟動後就一直執行直到使用者按下任意按鍵退出。執行多個消費者測試類,然後再執行生產者測試類,可用於檢視queue和topic的消費區別。如下為執行兩個測試類例項,然後生產者測試類傳送訊息後消費者消費訊息的結果:
圖3、第2個消費者的消費結果
說明:
(1)、圖2中的第一個“123123123”是之前單獨執行生產者時傳送的訊息,被本次執行的消費者給消費了;
(2)、圖2中的第二個“123123123”是兩個消費者執行起來後執行生產者傳送的訊息,對比圖3的消費結果可以看出來queue中的訊息只會被一個消費者消費;
(3)、圖2和圖3中的訊息“456789”是topic訊息,對比可以看出兩個消費者都收到了該主題訊息。
7、常見問題
(1)、ActiveMQ無法啟動
在使用ActiveMQ5.14的時候出現ActiveMQ無法啟動的問題,檢視啟動日誌(在安裝目錄的data目錄下),發現如下錯誤資訊:
WrapperSimpleApp: Unable to locate the class org.apache.activemq.console.Main: java.lang.UnsupportedClassVersionError: org/apache/activemq/console/Main : Unsupported major.minor version 51.0
這個問題是由於執行時的jdk與編譯class的jdk不一致造成的,解決辦法是更換jdk,具體使用什麼版本的jdk與activeMQ的版本有關係。我們可以在ActiveMQ的啟動指令碼中指定需要單獨使用的jdk版本。具體修改可以參考http://blog.csdn.net/smilefyx/article/details/49511975中指定tomcat的方法做修改。
(2)、執行時提示org.slf4j.impl.StaticLoggerBinder.getSingleton()Lorg/slf4j/impl/StaticLoggerBinder錯誤
在執行程式碼時出現如下類似的錯誤從而導致bean建立失敗,程式碼無法執行:
org.slf4j.impl.StaticLoggerBinder.getSingleton()Lorg/slf4j/impl/StaticLoggerBinder
該問題是由於缺少log庫或者log庫的版本不對,導致無法找到某些方法導致。本文中的解決辦法是新增如下依賴:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
8、原始碼下載:
原始碼可從如下地址進行下載:
相關文章
- flutter driver 整合測試Flutter
- Flutter 學習之路 - 測試(單元測試,Widget 測試,整合測試)Flutter
- SpringBoot整合測試Spring Boot
- spring boot 整合測試Spring Boot
- .Net單元測試xUnit和整合測試指南(1)
- spring 整合dubbo 測試搭建Spring
- 黑盒測試、白盒測試、單元測試、整合測試、系統測試、驗收測試的區別與聯絡...
- Mokito 單元測試與 Spring-Boot 整合測試Springboot
- 軟體測試---單元、整合、系統、驗收測試
- 測試你的前端程式碼 – part4(整合測試)前端
- Go 單元測試之Mysql資料庫整合測試GoMySql資料庫
- Tessy — 嵌入式軟體單元測試/ 整合測試工具
- .net持續整合測試篇之Nunit引數化測試
- .netcore持續整合測試篇之測試方法改造NetCore
- Java中的單元測試與整合測試最佳實踐Java
- Alluxio+HDFS+MapReduce整合及測試UX
- 使用Wiremock進行整合測試 - kubilayREMMock
- 整合測試時 MockMvc 無法注入MockMVC
- 使用Gradle執行整合測試Gradle
- 4大軟體測試策略的特點和區別(單元測試、整合測試、確認測試和系統測試)
- Tessy—支援複雜場景測試的單元整合測試工具
- SoapUI實踐:自動化測試、壓力測試、持續整合UI
- Linux 核心的持續整合測試Linux
- 使用遠端Docker進行整合測試Docker
- JMeter 如何與 MySQL 進行整合測試JMeterMySql
- ASP.NET Core Web API 整合測試ASP.NETWebAPI
- 如何開展大規模整合測試
- WinAMS--嵌入式軟體單元測試/整合測試工具
- SpringBoot2 整合測試元件,七種測試手段對比Spring Boot元件
- 大型專案裡Flutter測試應用例項整合測試深度使用Flutter
- .netcore持續整合測試篇之搭建記憶體伺服器進行整合測試一NetCore記憶體伺服器
- 「CI整合」基於Jest Mock API對業務邏輯整合測試MockAPI
- PouchContainer 整合測試覆蓋率統計AI
- SpringStateMachine狀態機之八-整合測試SpringMac
- 使用 Xcode Server 持續整合 & 打包測試XCodeServer
- 測試測試測試測試測試測試
- Python專案維護不了?可能是測試沒到位。Django的單元測試和整合測試初探PythonDjango
- .net持續整合測試篇之Nunit that斷言