一、异步信息-使用JMS
1、设置 JMS
pom.xml引入依赖:
如果使用ActiveMQ:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
ActiveMQ 特定的属性:
属性 | 描述 |
---|---|
spring.activemq.broker-url | Broker 的 URL |
spring.activemq.user | 用于访问 Broker 的用户(可选) |
spring.activemq.password | 用于访问 Broker 的密码(可选) |
spring.activemq.in-memory | 是否启动内存 Broker(默认:true) |
application.yml:
spring:
activemq:
# 使用单个属性 spring.activemq.broker-url 指定 ActiveMQ Broker 的地址
broker-url: tcp://testdemo.springbootlearn.com
user: learndemouser
password: 123456
如果使用 ActiveMQ Artemis:
Artemis 是 ActiveMQ 的下一代重新实现。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId>
</dependency>
Artemis 属性:
属性 | 描述 |
---|---|
spring.artemis.host | broker 主机 |
spring.artemis.port | broker 端口 |
spring.artemis.user | 用于访问 broker 的用户(可选) |
spring.artemis.password | 用于访问 broker 的密码(可选) |
application.yml:
spring:
artemis:
# 创建到监听 testdemo.springbootlearn.com(端口 61617)的 Artemis Broker 的 broker 连接
host: testdemo.springbootlearn.com
port: 61617
user: learndemouser
password: 123456
如果使用 ActiveMQ,则需要设置 spring.activemq.in-memory
属性为 false,以防止 Spring 启动内存中的 Broker。
2、使用 JmsTemplate 发送消息
JmsTemplate主要方法:
// 发送原始消息,需要一个 MessageCreator 来制造一个 Message 对象
void send(MessageCreator messageCreator) throws JmsException;
void send(Destination destination, MessageCreator messageCreator) throws JmsException;
void send(String destinationName, MessageCreator messageCreator) throws JmsException;
// 发送转换自对象的消息,接受一个 Object,并在后台自动将该 Object 转换为一条 Message
void convertAndSend(Object message) throws JmsException;
void convertAndSend(Destination destination, Object message) throws JmsException;
void convertAndSend(String destinationName, Object message) throws JmsException;
// 发送经过处理后从对象转换而来的消息,会自动将一个 Object 转换成一条 Message,
// 但也会接受一个 MessagePostProcessor,以便在 Message 发送前对其进行定制
void convertAndSend(Object message, MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(Destination destination, Object message, MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(String destinationName, Object message, MessagePostProcessor postProcessor) throws JmsException;
三个方法类别中的每一个都由三个重载的方法组成,它们是通过指定 JMS 目的地(队列或主题)的方式来区分的。
一个方法不接受目的地参数,并将消息发送到默认目的地。
一个方法接受指定消息目的地的目标对象。
一个方法接受一个 String,该 String 通过名称指定消息的目的地。
JmsOrderMessagingService
:
package xxx.springlearndemo.service.messaging;
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.Service;
@Service
public class JmsOrderMessagingService{
private JmsTemplate jms;
@Autowired
public JmsOrderMessagingService(JmsTemplate jms) {
this.jms = jms;
}
// 方法1:
// @Override
// public void sendOrder(Order order) {
// jms.send(new MessageCreator() {
// @Override
// public Message createMessage(Session session)
// throws JMSException {
// return session.createObjectMessage(order);
// }
// });
// }
// 方法2:
// lambda 表达式
@Override
public void sendOrder(Order order) {
jms.send(session -> session.createObjectMessage(order));
}
}
上面代码方法1,方法2中的 jms.send() 的调用没有指定目的地,
还必须使用 spring.jms.template.default-destination
属性指定一个默认的目的地名称。
可以在 application.yml 中设置属性:
spring:
jms:
template:
default-destination: springlearndemo.order.queue
如果需要将消息发送到缺省目的地之外的目的地,则需要将该目的地指定为 send() 方法的参数。
声明一个 Destination bean,然后将其注入执行消息传递的 bean。
package xxx.springlearndemo.service.messaging;
// Artemis的ActiveMQQueue类。
// 还有一个是 ActiveMQ的ActiveMQQueue类。org.apache.activemq.command。
import org.apache.activemq.artemis.jms.client;
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.Service;
@Service
public class JmsOrderMessagingService {
private JmsTemplate jms;
private Destination orderQueue;
// Destination bean 被注入到 JmsOrderMessagingService,
// 可以在调用 send() 时使用它来指定目的地
@Autowired
public JmsOrderMessagingService(JmsTemplate jms, Destination orderQueue) {
this.jms = jms;
this.orderQueue = orderQueue;
}
// ...
public Destination orderQueue() {
return new ActiveMQQueue("springlearndemo.order.queue");
}
@Override
public void sendOrder(Order order) {
jms.send(
orderQueue,
session -> session.createObjectMessage(order));
}
// 指定了目的地名称,将名称作为 send() 的第一个参数
@Override
public void sendOrder(Order order) {
jms.send(
"springlearndemo.order.com",
session -> session.createObjectMessage(order));
}
}
1)在发送前转换消息:
使用JmsTemplates的convertAndSend()
方法,不需要提供 MessageCreator。
package xxx.springlearndemo.service.messaging;
import javax.jms.JMSException;
import javax.jms.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import xxx.springlearndemo.Order;
@Service
public class JmsOrderMessagingService implements OrderMessagingService {
private JmsTemplate jms;
@Autowired
public JmsOrderMessagingService(JmsTemplate jms) {
this.jms = jms;
}
// convertAndSend() 将接受 Destination 或 String 值来指定目的地,
// 或者可以完全忽略目的地来将消息发送到默认目的地。
@Override
public void sendOrder(Order order) {
jms.convertAndSend("springlearndemo.order.queue", order,
this::addOrderSource);
}
// 自定义头信息
private Message addOrderSource(Message message) throws JMSException {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
}
}
2)配置消息转换器
MessageConverter接口提供了2个方法。
public interface MessageConverter {
Message toMessage(Object object, Session session)
throws JMSException, MessageConversionException;
Object fromMessage(Message message);
}
spring 已经提供了一些MessageConverter的实现如下:
其中SimpleMessageConverter 是默认的消息转换器。使用它的前提是发送的对象实现 Serializable 接口。
消息转换器 | 描述 |
---|---|
MappingJackson2MessageConverter | 使用 Jackson 2 JSON 库对消息进行与 JSON 的转换 |
MarshallingMessageConverter | 使用 JAXB 对消息进行与 XML 的转换 |
MessagingMessageConverter | 使用底层 MessageConverter(用于有效负载)和JmsHeaderMapper(用于将 Jms 信息头映射到标准消息标头)将 Message 从消息传递抽象转换为 Message,并从 Message 转换为 Message |
SimpleMessageConverter | 将 String 转换为 TextMessage,将字节数组转换为 BytesMessage,将 Map 转换为 MapMessage,将Serializable 转换为 ObjectMessage |
3)后期处理消息
使用 MessagePostProcessor 在消息发送之前添加 X_ORDER_SOURCE 头信息
jms.convertAndSend("springlearndemo.order.queue", order,
new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message)
throws JMSException {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
}
});
lambda :
jms.convertAndSend("springlearndemo.order.queue", order,
message -> {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
});
3、接收 JMS 消息
消费消息: 拉模型
推模型
。
JmsTemplate 几种接收消息都使用拉模型
。
1)使用 JmsTemplate 接收
JmsTemplate 的拉模型,拉模式的方法:
// 方法接收原始消息
Message receive() throws JmsException;
Message receive(Destination destination) throws JmsException;
Message receive(String destinationName) throws JmsException;
// 使用配置的消息转换器将消息转换为域类型
// 其中的每一个,可以指定 Destination 或包含目的地名称的 String,
// 也可以从缺省目的地获取一条消息
Object receiveAndConvert() throws JmsException;
Object receiveAndConvert(Destination destination) throws JmsException;
Object receiveAndConvert(String destinationName) throws JmsException;
使用receive():
JmsOrderReceiver
:
package xxx.springbootlearn.messaging.jms;
import javax.jms.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
@Component
public class JmsOrderReceiver {
private JmsTemplate jms;
private MessageConverter converter;
@Autowired
public JmsOrderReceiver(JmsTemplate jms, MessageConverter converter) {
this.jms = jms;
this.converter = converter;
}
// 使用 receive()
public Order receiveOrder() {
// 使用了一个 String 来指定从何处拉取订单
Message message = jms.receive("springlearndemo.order.queue");
// Object 返回的,在返回它之前需要进行转换
return (Order) converter.fromMessage(message);
}
}
使用 receiveAndConvert():
这样就不需要注入 MessageConverter,因为所有的消息转换都将在 receiveAndConvert() 中完成。
package xxx.springbootlearn.messaging.jms;
import javax.jms.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
@Component
public class JmsOrderReceiver {
private JmsTemplate jms;
@Autowired
public JmsOrderReceiver(JmsTemplate jms) {
this.jms = jms;
}
// 使用 receiveAndConvert()
public Order receiveOrder() {
return (Order) jms.receiveAndConvert("springlearndemo.order.queue");
}
}
2)声明消息监听器
与拉模型不同,消息监听器是一个被动组件,在消息到达之前是空闲的。
@JmsListener 注解
:
package xxx.springbootlearn.messaging.jms.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class OrderListener {
//...
// 被动地监听消息
// Spring 中的框架代码将等待消息到达指定的目的地,
// 当消息到达时,receiveOrder() 方法将自动调用,并将消息的 Order 作为参数
@JmsListener(destination = "springlearndemo.order.queue")
public void receiveOrder(Order order) {
// ...
}
}