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的消费区别。如下为运行两个测试类实例,然后生产者测试类发送消息后消费者消费消息的结果:

图2、第1个消费者的消息消费结果

图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、源码下载:

源码可从如下地址进行下载:

http://download.csdn.net/detail/yxtouch/9835989

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值