【前言】Java消息服务(JMS)广泛应用在跨系统的异步通信中,优势在于减少响应时间和降低耦合,通常我们会把比较耗时且不需要立即返回结果的操作放入消息队列中。
【分类】1.点对点消息模型 :每个消息只有一个接受者,消息发送和消息接收者之间无直接关联。当消息发送者发送消息时,无论接收者在不在运行,都能获取到消息,并在收到时发送确认消息。
2.发布订阅模型:一个消息可以有多个订阅者,发布、订阅之间有明显的时间依赖关系,客户端创建订阅后才能接收消息,订阅者需要一直保持活动状态才能接收到消息。可以做一个持久化订阅,即时订阅者还没有被激活,也能接收发布的消息。
【整合项目构建】下面简要介绍下active mq与Spring的项目整合过程
项目目录(整体采用Maven工程构建):
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nbu.activemq</groupId>
<artifactId>MQ_Active</artifactId>
<version>1.0-SNAPSHOT</version>
<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>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.2.RELEASE</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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
</project>
spring 配置文件app-jms.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"
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="Consumer" />
<context:component-scan base-package="Producer" />
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<!-- 可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供
真正产生到JMS服务器链接的ConnectionFactory还得是由JMS服务厂商提供,
并且需要把它注入到Spring提供的ConnectionFactory中。
-->
<!--<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
</bean>-->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
<property name="sessionCacheSize" value="100" />
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean id="producerService" class="Producer.ProducerServiceImpl">
<property name="jmsTemplate" ref="jmsTemplate"></property>
<property name="destination" value="queueDestination"></property>
</bean>
<!-- 消息监听容器 -->
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="transacted"
concurrency="10-50">
<jms:listener destination="queueDestination" ref="consumerMessageListener"
method="onMessage" />
</jms:listener-container>
<!-- 消息监听器 -->
<bean id="consumerMessageListener" class="Consumer.ConsumerMessageListener"/>
</beans>
pojo:
public class Person implements Serializable{
public String name;
public int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
消息生产者接口:
public interface ProducerService {
/**
* 使用mq默认destination
* @param destination
* @param message
*/
public void sendMessage(Destination destination, final String message);
/**
* 自定义destination
* @param message
*/
public void sendMessage2(final String message);
/**
*自定义destination + Object对象消息
* @param person
*/
public void sendMessage3(final Person person);
}
接口实现:
import javax.jms.*;
import javax.annotation.Resource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
import pojo.Person;
@Component
public class ProducerServiceImpl implements ProducerService {
private JmsTemplate jmsTemplate;
/**
* @Param destination: 可以使用mq自带queue,并构造注入一个name
* 2.可以自定义destination,生产者初始化时指定该值,消息者在消费时从该destination上取到消息
*/
private String destination;
//destination入参
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);
}
});
}
//destination不入参
public void sendMessage2(final String message) {
System.out.println("---------------生产者发送消息-----------------");
System.out.println("---------------生产者发了一个消息:" + message);
jmsTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
}
@Override
public void sendMessage3(final Person person) {
System.out.println("---------------生产者发送消息-----------------");
System.out.println("---------------生产者发了一个消息:" + JSON.toJSONString(person));
jmsTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(JSON.toJSONString(person,SerializerFeature.BrowserCompatible, SerializerFeature.WriteClassName));
}
});
}
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
@Resource
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
}
消息监听器(接收者):
import com.alibaba.fastjson.JSON;
import pojo.Person;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* JMS消息监听器是消息的默认事件处理者,实现了MessageListener接口,该接口包含一个onMessage方法,
* 在该方法中需要定义消息达到后的具体动作。通过调用setMessageListener方法我们给指定消费者定义了消息监听器
*/
public class ConsumerMessageListener implements MessageListener {
ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
public void onMessage(Message message) {
//这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换
if (message instanceof TextMessage) {
try {
final Object msg = JSON.parse(((TextMessage) message).getText());
if (msg instanceof Person) {
executorService.submit(new Runnable() {
@Override
public void run() {
Person p = (Person) msg;
System.out.println("接收到消息");
try {
System.out.println("消息内容是:" + p.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}catch (Exception e){
}
}
}
/* public void onMessage(final Message message) {
//这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换
executorService.submit(new Runnable() {
@Override
public void run() {
TextMessage textMsg = (TextMessage) message;
System.out.println("接收到一个纯文本消息。");
try {
System.out.println("消息内容是:" + textMsg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}*/
}
测试类:
import Producer.ProducerService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pojo.Person;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring/app-jms2.xml")
public class ProducerConsumerTest2 {
@Autowired
private ProducerService producerService;
@Test
public void testSend2() {
for (int i=0; i<10; i++) {
producerService.sendMessage2( "你好,生产者!这是消息:" + (i+1));
}
}
@Test
public void testSend3() {
for (int i=0; i<10; i++) {
Person p = new Person("dh",i+1) ;
producerService.sendMessage3(p);
}
}
@Test
public void jsonTest(){
Person p = new Person();
p.setAge(10);
//{"age":10,"name":null}
//{"age":10}
System.out.println(JSON.toJSONString(p, SerializerFeature.WriteMapNullValue));
System.out.println(JSON.toJSONString(p, SerializerFeature.WriteNullStringAsEmpty));
}
}
实验结果:
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":1,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":2,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":3,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":4,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":5,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":6,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":7,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":8,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":9,"name":"dh"}
---------------生产者发送消息-----------------
---------------生产者发了一个消息:{"age":10,"name":"dh"}
接收到消息
消息内容是:Person{name='dh', age=1}
接收到消息
消息内容是:Person{name='dh', age=9}
接收到消息
消息内容是:Person{name='dh', age=7}
接收到消息
消息内容是:Person{name='dh', age=2}
接收到消息
消息内容是:Person{name='dh', age=8}
接收到消息
消息内容是:Person{name='dh', age=3}
接收到消息
消息内容是:Person{name='dh', age=4}
接收到消息
消息内容是:Person{name='dh', age=5}
接收到消息
消息内容是:Person{name='dh', age=6}
接收到消息
消息内容是:Person{name='dh', age=10}
【总结】本文只对点对点的消息模型进行配置,可见通过Spring可简化项目的开发,提高项目开发效率。