异步接收消息的最简单方法是使用带批注的侦听器 端点基础结构。简而言之,它允许您公开托管方法 bean 作为 JMS 侦听器端点。以下示例演示如何使用它:

@Component
public class MyService {

	@JmsListener(destination = "myDestination")
	public void processOrder(String data) { ... }
}

前面示例的思想是,每当 上有消息可用时,就会调用该方法 因此(在本例中,使用 JMS 消息的内容,类似于 MessageListenerAdapter 提供的内容)。jakarta.jms.DestinationmyDestinationprocessOrder

带注释的端点基础结构创建消息侦听器容器 每个带注释的方法的幕后花絮,通过使用 . 此类容器不会针对应用程序上下文注册,但可以很容易地注册 通过使用 Bean 进行管理。JmsListenerContainerFactoryJmsListenerEndpointRegistry

@JmsListener是 Java 8 上的可重复注解,因此您可以关联 通过向其添加其他声明,使用相同的方法使用多个 JMS 目标。@JmsListener

启用侦听器终结点注释

要启用对批注的支持,您可以添加到以下之一 您的类,如以下示例所示:@JmsListener@EnableJms@Configuration

@Configuration
@EnableJms
public class AppConfig {

	@Bean
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setConnectionFactory(connectionFactory());
		factory.setDestinationResolver(destinationResolver());
		factory.setSessionTransacted(true);
		factory.setConcurrency("3-10");
		return factory;
	}
}

缺省情况下,基础结构会查找名为工厂源的 Bean,以用于创建消息侦听器容器。在这个 情况下(忽略 JMS 基础结构设置),您可以调用核心轮询大小为 3 个线程且最大池大小为 10 个线程的方法。jmsListenerContainerFactoryprocessOrder

您可以自定义侦听器容器工厂以用于每个批注,也可以 通过实现接口来配置显式默认值。 仅当注册了至少一个没有特定端点的终端节点时,才需要默认值 集装箱工厂。有关详细信息和示例,请参阅实现 JmsListenerConfigurer 的类的 javadoc。JmsListenerConfigurer

如果更喜欢 XML 配置,则可以使用该元素,如以下示例所示:<jms:annotation-driven>

<jms:annotation-driven/>

<bean id="jmsListenerContainerFactory"
		class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
	<property name="connectionFactory" ref="connectionFactory"/>
	<property name="destinationResolver" ref="destinationResolver"/>
	<property name="sessionTransacted" value="true"/>
	<property name="concurrency" value="3-10"/>
</bean>

编程终结点注册

JmsListenerEndpoint提供 JMS 端点的模型,并负责配置 该模型的容器。基础结构允许您以编程方式配置终结点 除了注释检测到的那些。 以下示例演示如何执行此操作:JmsListener

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

	@Override
	public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
		SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
		endpoint.setId("myJmsEndpoint");
		endpoint.setDestination("anotherQueue");
		endpoint.setMessageListener(message -> {
			// processing
		});
		registrar.registerEndpoint(endpoint);
	}
}

在前面的示例中,我们使用了 ,它提供了要调用的实际值。但是,您也可以构建自己的终端节点变体 来描述自定义调用机制。SimpleJmsListenerEndpointMessageListener

请注意,您可以完全跳过 并以编程方式仅通过 注册终结点。@JmsListenerJmsListenerConfigurer

带注释的终结点方法签名

到目前为止,我们一直在端点中注入一个简单的,但它实际上可以 具有非常灵活的方法签名。在下面的示例中,我们重写它以注入 自定义标头:StringOrder

@Component
public class MyService {

	@JmsListener(destination = "myDestination")
	public void processOrder(Order order, @Header("order_type") String orderType) {
		...
	}
}

可以在 JMS 侦听器端点中注入的主要元素如下:

  • raw 或其任何子类(前提是 匹配传入消息类型)。jakarta.jms.Message

  • 对于对本机 JMS API 的可选访问(例如,用于发送 自定义回复)。jakarta.jms.Session

  • 表示传入的 JMS 消息。 请注意,此消息包含自定义标头和标准标头(如定义 由 )。org.springframework.messaging.MessageJmsHeaders

  • @Header-annotated 方法参数,用于提取特定标头值,包括 标准 JMS 标头。

  • 一个 -annotated 参数,该参数也必须可赋给 for 访问所有标头。@Headersjava.util.Map

  • 不属于受支持类型( 或 )之一的未批注元素被视为有效负载。您可以通过注释来明确这一点 带有 的参数。您还可以通过添加额外的 .MessageSession@Payload@Valid

注入 Spring 抽象的能力特别有用,可以受益 从存储在特定于传输的消息中的所有信息,而无需依赖 特定于传输的 API。以下示例演示如何执行此操作:Message

@JmsListener(destination = "myDestination")
public void processOrder(Message<Order> order) { ... }

方法参数的处理由 提供,您可以 进一步自定义以支持其他方法参数。您可以自定义转换和验证 那里的支持也是如此。DefaultMessageHandlerMethodFactory

例如,如果我们想在处理它之前确保我们是有效的,我们可以 使用注释有效负载并配置必要的验证程序,如以下示例所示:Order@Valid

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

	@Override
	public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
		registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory());
	}

	@Bean
	public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
		DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
		factory.setValidator(myValidator());
		return factory;
	}
}

响应管理

MessageListenerAdapter 中的现有支持已允许方法具有非返回类型。在这种情况下,结果 调用封装在 中,在指定的目标中发送 在原始邮件的标头中或在 侦听器。现在,您可以使用 消息传递抽象。voidjakarta.jms.MessageJMSReplyTo@SendTo

假设我们的方法现在应该返回一个 ,我们可以写 自动发送响应,如以下示例所示:processOrderOrderStatus

@JmsListener(destination = "myDestination")
@SendTo("status")
public OrderStatus processOrder(Order order) {
	// order processing
	return status;
}
如果有多个 -annotated 方法,还可以将批注放在类级别以共享默认的回复目标。@JmsListener@SendTo

如果需要以与传输无关的方式设置其他标头,则可以使用类似于以下方法返回 a:Message

@JmsListener(destination = "myDestination")
@SendTo("status")
public Message<OrderStatus> processOrder(Order order) {
	// order processing
	return MessageBuilder
			.withPayload(status)
			.setHeader("code", 1234)
			.build();
}

如果需要在运行时计算响应目标,可以封装响应 在还提供要在运行时使用的目标的实例中。我们可以重写以前的 示例如下:JmsResponse

@JmsListener(destination = "myDestination")
public JmsResponse<Message<OrderStatus>> processOrder(Order order) {
	// order processing
	Message<OrderStatus> response = MessageBuilder
			.withPayload(status)
			.setHeader("code", 1234)
			.build();
	return JmsResponse.forQueue(response, "status");
}

最后,如果您需要为响应指定一些 QoS 值,例如优先级或 生活的时间,可以相应地配置, 如以下示例所示:JmsListenerContainerFactory

@Configuration
@EnableJms
public class AppConfig {

	@Bean
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setConnectionFactory(connectionFactory());
		QosSettings replyQosSettings = new QosSettings();
		replyQosSettings.setPriority(2);
		replyQosSettings.setTimeToLive(10000);
		factory.setReplyQosSettings(replyQosSettings);
		return factory;
	}
}