引言
使用artemis版本MQ。Spring JMS 使用springframework-mvc架构,传统xml配置,部署到tomcat运行。如果采用springboot将更简单。有空将更新成springboot
ActiveMQ基本安装与使用
注: ActiveMQ下载的artemis版本,与之前的ActiveMQ版本有点区别,
下载ActiveMQ
下载地址 https://activemq.apache.org/index.html
## 安装ActiveMQ
- apache-artemis-2.11.0.bin.tar.gz解压该文件
- 进入cd apache-artemis-2.11.0/bin目录
- 运行一下脚本,创建一个实例,自动在software目录下生成一个myInstance目录,
./artemis create /Users/somnus/Documents/software/myInstance
# ./artemis create /Users/somnus/Documents/software/myInstance
- 运行activemq
cd ./artemis create /Users/somnus/Documents/software/myInstance/bin
./artemis run #运行activemq
5.通过浏览器访问activemq控制台
地址 http://localhost:8161/
6. 进入控制台Management Console
路径:http://localhost:8161/console/login,记不住,直接http://localhost:8161通过点击进入
Spring JMS项目
项目POM配置
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- 用于消息转换 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- jms客户端-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jms-client</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jms -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
</dependencies>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- <listener>-->
<!-- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
<!-- </listener>-->
<!-- 默认加载 /WEB-INF/applicationContext.xml-->
<!-- <context-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>/WEB-INF/applicationContext.xml</param-value>-->
<!-- </context-param>-->
<servlet>
<servlet-name>app</servlet-name>
<!-- 默认加载 namespace-servlet.xml配置文件
这里是app-servlet.xml -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
springmvc配置
配置文件名:app-servlet.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:mvc="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<mvc:component-scan base-package="com.teachayu.activemq" />
<!-- 链接工厂 -->
<bean id="connectionFactory" class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory" />
<!-- 缓存JMS工厂 -->
<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="connectionFactory"/>
</bean>
<!-- jms消息转换器 JmsTemplate发送与接收 -->
<bean id="messageConverter" class="org.springframework.jms.support.converter.MappingJackson2MessageConverter" >
<property name="typeIdPropertyName" value="test" />
<property name="typeIdMappings" >
<map>
<entry key="test" value="com.teachayu.activemq.controller.UserEntity"/>
</map>
</property>
</bean>
<!--点对点 文本消息发送模板 -->
<bean id="pointJmsTemplate" class="org.springframework.jms.core.JmsTemplate" >
<constructor-arg ref="connectionFactory"></constructor-arg>
<!-- 定义默认队列-->
<property name="defaultDestinationName" value="test"/>
<property name="messageConverter" ref="messageConverter"/>
<!--pubSubDomain true: Publish/Subscribe false: Point-to-Point-->
<!-- 默认 Point-to-Point -->
<property name="pubSubDomain" value="false" />
</bean>
<!-- 文本消息监听器 接收文本消息 -->
<bean id="receiveListener" class="com.teachayu.activemq.controller.TextReceiveListener"></bean>
<!--接收文本消息 -->
<bean id="defaultMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="messageListener" ref="receiveListener"/>
<property name="messageConverter" ref="messageConverter"/>
<property name="destinationName" value="test"/>
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<!-- 对象消息转换器 -->
<bean id="pojoMessageConverter" class="com.teachayu.activemq.controller.PojoMessageConverter"/>
<!--点对点 发送消息 使用另一个转换器,基于对象-->
<bean id="pojoJmsTemplate" class="org.springframework.jms.core.JmsTemplate" >
<constructor-arg ref="connectionFactory"></constructor-arg>
<!-- 定义默认队列-->
<property name="defaultDestinationName" value="test"/>
<property name="messageConverter" ref="pojoMessageConverter"/>
<!--pubSubDomain true: Publish/Subscribe false: Point-to-Point-->
<!-- 默认 Point-to-Point -->
<property name="pubSubDomain" value="false" />
</bean>
<!-- 对象消息监听器 -->
<bean class="com.teachayu.activemq.controller.Pojo1ReceiveListener" id="pojo1ReceiveListener"/>
<!-- 接收对象消息 -->
<!--把消息监听器注册到 -->
<!-- SimpleMessageListenerContainer -->
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer" >
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="pojo1"/>
<property name="messageConverter" ref="pojoMessageConverter"/>
<property name="messageListener" ref="pojo1ReceiveListener"/>
</bean>
<!--发布与订阅 -->
<bean id="pubJmsTemplate" class="org.springframework.jms.core.JmsTemplate" >
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<!-- 定义默认队列-->
<property name="defaultDestinationName" value="pub"/>
<property name="messageConverter" ref="pojoMessageConverter"/>
<!--pubSubDomain true: Publish/Subscribe false: Point-to-Point-->
<!-- 默认 Point-to-Point -->
<property name="pubSubDomain" value="true" />
</bean>
<!-- 订阅消息 Subscribe1Listener -->
<bean class="com.teachayu.activemq.controller.Subscribe1Listener" id="subscribe1Listener"/>
<!-- 订阅消息 Subcribe2Listener -->
<bean class="com.teachayu.activemq.controller.Subscribe2Listener" id="subscribe2Listener"/>
<bean id="listenerContainer1" class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<!-- pubSubDomain true:发布订阅模式 -->
<property name="pubSubDomain" value="true"/>
<property name="destinationName" value="pub"/>
<property name="messageListener" ref="subscribe1Listener"/>
<property name="messageConverter" ref="pojoMessageConverter"/>
<property name="connectionFactory" ref="cachingConnectionFactory"/>
</bean>
<bean id="listenerContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!-- pubSubDomain true:发布订阅模式 -->
<property name="pubSubDomain" value="true"/>
<property name="messageListener" ref="subscribe2Listener"/>
<property name="messageConverter" ref="pojoMessageConverter"/>
<property name="destinationName" value="pub"/>
<property name="connectionFactory" ref="cachingConnectionFactory"/>
</bean>
<!--http请求转json -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list><value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
</beans>
消息转换器 与实体类
定义PojoMessageConverter消息转换器,将Object转成ObjectMessage消息,或将ObjectMessage消息转成Object。Object将实现Serializable接口
package com.teachayu.activemq.controller;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import java.io.Serializable;
public class PojoMessageConverter implements MessageConverter {
@Override
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
return session.createObjectMessage((Serializable) object);
}
@Override
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
return ((ObjectMessage)message).getObject();
}
}
实体类,用于测试
package com.teachayu.activemq.controller;
import java.io.Serializable;
public class UserEntity implements Serializable {
private String name;
private int age;
private String address;
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "UserEntity{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
生产者
发送消息与发布消息,通过http://localhost:8080/activemq/send 完成简单消息推送
package com.teachayu.activemq.controller;
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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
/**
* 使用Point-to-Point模式发送消息
* 使用Publish/Subscribe 模式发布消息
*/
@RestController
public class ProducerController {
/**
* 发送文本消息模板 Point-to-Point
* MappingJackson2MessageConverter转换器
*/
private JmsTemplate jmsTemplate;
/**
* 发送对象消息 Point-to-Point
* PojoMessageConverterz转换器
*/
private JmsTemplate pojoJmsTemplate;
/**
* 发布对象消息 Publish/Subscribe
* PojoMessageConverterz转换器
*/
private JmsTemplate pubJmsTemplate;
@RequestMapping("/send")
public String send(){
/**
* Point-to-Point 模式
* 使用默认队列发送消息
* 使用JmsTemplate同步接收
*/
jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
//发送TextMessage消息
return session.createTextMessage("text队列发送消息(接收同步):"+System.currentTimeMillis());
}
});
/**
* Point-to-Point 模式
* 使用默认队列发送消息
* 异步接收 MessageListener
*/
jmsTemplate.send("text1", new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
//发送TextMessage消息
return session.createTextMessage("text1队列发送消息(接收异步)"+System.currentTimeMillis());
}
});
/**
* Point-to-Point 模式
* 使用定义的转换器
* org.springframework.jms.support.converter.MappingJackson2MessageConverter
* 使用pojo队列发送消息
* 使用JmsTemplate同步接收
*/
UserEntity userEntity = new UserEntity();
long l = System.currentTimeMillis();
userEntity.setName("张三"+l);
userEntity.setAddress("地址"+l);
userEntity.setAge(1);
jmsTemplate.convertAndSend("pojo",userEntity);
/**
* Point-to-Point 模式
* 使用定义的转换器 com.teachayu.activemq.controller.PojoMessageConverter
* 使用pojo队列发送消息
* 异步接收 MessageListener
*/
userEntity = new UserEntity();
l = System.currentTimeMillis();
userEntity.setName("张三"+l);
userEntity.setAddress("地址"+l);
userEntity.setAge(1);
pojoJmsTemplate.convertAndSend("pojo1",userEntity);
/**
* Publish/Subscribe 模式
* 发布消息
* 默认队列 pub ,配置文件中
*/
userEntity = new UserEntity();
l = System.currentTimeMillis();
userEntity.setName("张三"+l);
userEntity.setAddress("地址"+l);
userEntity.setAge(1);
pubJmsTemplate.convertAndSend("pub",userEntity);
return "hello";
}
@Autowired
@Qualifier("pointJmsTemplate")
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
@Autowired
@Qualifier("pojoJmsTemplate")
public void setPojoJmsTemplate(JmsTemplate pojoJmsTemplate) {
this.pojoJmsTemplate = pojoJmsTemplate;
}
@Autowired
@Qualifier("pubJmsTemplate")
public void setPubJmsTemplate(JmsTemplate pubJmsTemplate) {
this.pubJmsTemplate = pubJmsTemplate;
}
}
同步消费者
同步接收消息,是阻塞模式,通过http://localhost:8080/activemq/receive接收消息。
注意:一定要先执行send请求,完成消费发送,不是receive请求将阻塞
package com.teachayu.activemq.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.jms.JMSException;
import javax.jms.TextMessage;
/**
* 阻塞模式接收消息
*/
@RestController
public class CustomerController {
private JmsTemplate jmsTemplate;
@RequestMapping("receive")
public UserEntity receive() throws JMSException {
//阻塞接收消息,默认队列,队列中无消息将阻塞
TextMessage message = (TextMessage)jmsTemplate.receive();
System.out.println("接收test队列:"+message.getText());
//阻塞接收消息,pojo队列,队列中无消息将阻塞
UserEntity userEntity = (UserEntity)jmsTemplate.receiveAndConvert("pojo");
System.out.println("接收pojo队列消息"+ userEntity);
return userEntity;
}
@Autowired
@Qualifier("pointJmsTemplate")
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
}
异步接收消息,监听模式
采用监听模式接收消息,有消息来将自动获取消息
文本消息接收
package com.teachayu.activemq.controller;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* 监听接收消息
*/
public class TextReceiveListener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
String text = ((TextMessage) message).getText();
System.out.println("接收到test队列消息:"+text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
对象消息接收
package com.teachayu.activemq.controller;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
/**
* 队列监听器 pojo1队列
* PojoMessageConverter完成转换
*/
public class Pojo1ReceiveListener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
UserEntity userEntity = (UserEntity) ((ObjectMessage) message).getObject();
System.out.println("接收pojo1队列消息:"+ message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
订阅消息 监听模式
Subscribe1Listener 订阅pub主题消息
package com.teachayu.activemq.controller;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
/**
* 订阅消息
*/
public class Subscribe1Listener implements MessageListener {
@Override
public void onMessage(Message message) {
UserEntity userEntity = null;
try {
userEntity = (UserEntity) ((ObjectMessage) message).getObject();
} catch (JMSException e) {
e.printStackTrace();
}
System.out.println("Subscribe1Listener订阅pub队列消息:" + userEntity);
}
}
Subscribe2Listener 订阅pub主题消息
package com.teachayu.activemq.controller;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
/**
* 订阅消息
*/
public class Subscribe2Listener implements MessageListener {
@Override
public void onMessage(Message message) {
UserEntity userEntity = null;
try {
userEntity = (UserEntity) ((ObjectMessage) message).getObject();
} catch (JMSException e) {
e.printStackTrace();
}
System.out.println("Subscribe2Listener订阅pub队列消息:" + userEntity);
}
}
测试
前提是运行activemq与springJMS项目,将可以进行以下测试。测试就不截图了。
发送消息
通过浏览器访问 http://localhost:8080/activemq/send 将完成所有消息发送。
接收消息
- 采用MessageListener方式在控制台自动打印接收到消息
- 通过访问http://localhost:8080/activemq/receive接收text与pojo队列消息
参考资料
https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/integration.html#remoting-jms
http://localhost:8161/user-manual/index.html #该地址是安装activemq之后访问可以看到。
或者通过以下官网地址查看文档
https://activemq.apache.org/components/artemis/documentation/