发送消息

JmsTemplate 包含许多用于发送消息的便捷方法。其中一些发送方法通过使用 jakarta.jms.Destination 对象来指定目标,另一些则通过使用 JNDI 查找中的 String 来指定目标。不带目标参数的 send 方法会使用默认目标。spring-doc.cadn.net.cn

以下示例使用 MessageCreator 回调,从提供的 Session 对象创建一条文本消息:spring-doc.cadn.net.cn

import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.Queue;
import jakarta.jms.Session;

import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {

	private JmsTemplate jmsTemplate;
	private Queue queue;

	public void setConnectionFactory(ConnectionFactory cf) {
		this.jmsTemplate = new JmsTemplate(cf);
	}

	public void setQueue(Queue queue) {
		this.queue = queue;
	}

	public void simpleSend() {
		this.jmsTemplate.send(this.queue, new MessageCreator() {
			public Message createMessage(Session session) throws JMSException {
				return session.createTextMessage("hello queue world");
			}
		});
	}
}

在前面的示例中,JmsTemplate 是通过传入一个 ConnectionFactory 的引用来构造的。另一种方式是使用无参构造函数,并提供 connectionFactory 属性,以便以 JavaBean 的风格(使用 BeanFactory 或普通 Java 代码)来构造该实例。此外,也可以考虑继承 Spring 提供的便捷基类 JmsGatewaySupport,它已预置了用于 JMS 配置的 bean 属性。spring-doc.cadn.net.cn

send(String destinationName, MessageCreator creator) 方法允许你使用目标的字符串名称来发送消息。如果这些名称已在 JNDI 中注册,则应将模板的 destinationResolver 属性设置为 JndiDestinationResolver 的一个实例。spring-doc.cadn.net.cn

如果你创建了 JmsTemplate 并指定了一个默认目的地,那么 send(MessageCreator c) 方法会将消息发送到该目的地。spring-doc.cadn.net.cn

使用 JMS 消息转换器

为了便于发送领域模型对象,JmsTemplate 提供了多种发送方法,这些方法接受一个 Java 对象作为消息数据内容的参数。JmsTemplate 中重载的 convertAndSend()receiveAndConvert() 方法将转换过程委托给 MessageConverter 接口的实例。该接口定义了一个简单的契约,用于在 Java 对象和 JMS 消息之间进行转换。默认实现(SimpleMessageConverter)支持 StringTextMessagebyte[]BytesMessage 以及 java.util.MapMapMessage 之间的转换。通过使用转换器,您和您的应用程序代码可以专注于通过 JMS 发送或接收的业务对象,而无需关心其如何表示为 JMS 消息的细节。spring-doc.cadn.net.cn

沙箱当前包含一个 MapMessageConverter,它使用反射在 JavaBean 和 MapMessage 之间进行转换。你也可以自行实现其他常用的选择,例如使用现有的 XML 序列化包(如 JAXB 或 XStream)来创建表示该对象的 TextMessagespring-doc.cadn.net.cn

为了支持对消息的属性、头信息和正文进行设置(这些内容无法被通用地封装在转换器类中),MessagePostProcessor 接口允许您在消息完成转换之后、发送之前对其进行访问。以下示例展示了在将 java.util.Map 转换为消息后,如何修改消息的头信息和属性:spring-doc.cadn.net.cn

import java.util.HashMap;
import java.util.Map;

import jakarta.jms.JMSException;
import jakarta.jms.Message;

import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessagePostProcessor;

public class JmsSenderWithConversion {

	private JmsTemplate jmsTemplate;

	public void sendWithConversion() {
		Map<String, Object> map = new HashMap<>();
		map.put("Name", "Mark");
		map.put("Age", 47);
		jmsTemplate.convertAndSend("testQueue", map, new MessagePostProcessor() {
			public Message postProcessMessage(Message message) throws JMSException {
				message.setIntProperty("AccountID", 1234);
				message.setJMSCorrelationID("123-00001");
				return message;
			}
		});
	}

}

这将生成如下形式的消息:spring-doc.cadn.net.cn

MapMessage={
	Header={
		... standard headers ...
		CorrelationID={123-00001}
	}
	Properties={
		AccountID={Integer:1234}
	}
	Fields={
		Name={String:Mark}
		Age={Integer:47}
	}
}
这种特定于 JMS 的 org.springframework.jms.support.converter.MessageConverter 安排作用于 JMS 消息类型,负责直接转换为 jakarta.jms.TextMessagejakarta.jms.BytesMessage 等。若需支持通用消息负载的契约,请改用 org.springframework.messaging.converter.MessageConverter, 并配合 JmsMessagingTemplate 或更推荐使用 JmsClient 作为您的核心委托。

使用SessionCallbackProducerCallback on JmsTemplate

虽然发送操作涵盖了许多常见的使用场景,但有时您可能希望对 JMS SessionMessageProducer 执行多个操作。SessionCallbackProducerCallback 分别暴露了 JMS 的 Session 以及 Session/MessageProducer 对。JmsTemplate 上的 execute() 方法会运行这些回调方法。spring-doc.cadn.net.cn

发送消息使用JmsClient

import jakarta.jms.ConnectionFactory;

import org.springframework.jms.core.JmsClient;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

public class JmsClientSample {

	private final JmsClient jmsClient;

	public JmsClientSample(ConnectionFactory connectionFactory) {
		// For custom options, use JmsClient.builder(ConnectionFactory)
		this.jmsClient = JmsClient.create(connectionFactory);
	}

	public void sendWithConversion() {
		this.jmsClient.destination("myQueue")
				.withTimeToLive(1000)
				.send("myPayload");  // optionally with a headers Map next to the payload
	}

	public void sendCustomMessage() {
		Message<?> message = MessageBuilder.withPayload("myPayload").build();  // optionally with headers
		this.jmsClient.destination("myQueue")
				.withTimeToLive(1000)
				.send(message);
	}
}

后处理传出消息

应用程序通常需要在消息发送出去之前对其进行拦截,例如为所有传出的消息添加消息属性。 基于 spring-messaging org.springframework.messaging.core.MessagePostProcessorMessage 可以实现这一功能, 当它被配置到 JmsClient 上时,将应用于所有通过 sendsendAndReceive 方法发送的传出消息。spring-doc.cadn.net.cn

以下是一个拦截器向所有传出消息添加“tenantId”属性的示例。spring-doc.cadn.net.cn

import jakarta.jms.ConnectionFactory;

import org.springframework.jms.core.JmsClient;
import org.springframework.messaging.Message;
import org.springframework.messaging.core.MessagePostProcessor;
import org.springframework.messaging.support.MessageBuilder;

public class JmsClientWithPostProcessor {

	private final JmsClient jmsClient;

	public JmsClientWithPostProcessor(ConnectionFactory connectionFactory) {
		this.jmsClient = JmsClient.builder(connectionFactory)
				.messagePostProcessor(new TenantIdMessageInterceptor("42"))
				.build();
	}

	public void sendWithPostProcessor() {
		this.jmsClient.destination("myQueue")
				.withTimeToLive(1000)
				.send("myPayload");
	}

	static class TenantIdMessageInterceptor implements MessagePostProcessor {

		private final String tenantId;

		public TenantIdMessageInterceptor(String tenantId) {
			this.tenantId = tenantId;
		}

		@Override
		public Message<?> postProcessMessage(Message<?> message) {
			return MessageBuilder.fromMessage(message)
					.setHeader("tenantId", this.tenantId)
					.build();
		}
	}
}