Spring 4.1 即将推出的 JMS 改进

工程 | Stéphane Nicoll | 2014 年 4 月 30 日 | ...

Spring Framework 4.0 引入了一个新的 spring-messaging 模块,添加了一系列 Spring Integration 类型,例如核心的 Message 抽象。Spring 4.1 调整了其 JMS 支持,以便您可以从该抽象中受益。但在深入探讨之前,我想向您详细展示我们如何进一步改进了监听器端点基础设施。

注解驱动的监听器端点

您可能习惯了 <xyz:annotation-driven> 元素或 @Enable* 对应项,或许您一直在寻找 JMS 的类似功能。现在就不用再找了:Spring framework 的下一个主要版本将允许您使用简单的注解来定义 JMS 监听器。

@Component
public class MyService {

    @JmsListener(containerFactory = "myContainerFactory", destination = "myQueue")
    public void processOrder(String data) { ... }

}

以下配置(忽略 JMS 基础设施设置)会在内部在 myQueue 目标上创建一个 JMS 消息监听器容器,并在消息可用时调用 processOrder

@Configuration
@EnableJms
public class AppConfig {
	
    @Bean
    public DefaultJmsListenerContainerFactory myContainerFactory() {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        factory.setDestinationResolver(destinationResolver());
        factory.setConcurrency("3-10");
        return factory;
    }
}

这是使用 XML 命名空间的等效配置

<jms:annotation-driven/>

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

像往常一样,@JmsListener 可以直接放在方法上,或通过元注解间接使用。该注解具有 jms:listener XML 元素提供已久的一些常用选项。然而,containerFactory 是新的,它引用 JmsListenerContainerFactory 的名称,这相当于您过去在 <jms:listener-container> 元素中配置的内容。

如果您想从现有配置平滑过渡,我们为该元素添加了一个 factory-id 属性。当它存在时,该配置将自动以该名称暴露为一个 JmsListenerContainerFactory bean。此 XML 配置等同于上面的 myJmsContainerFactory bean

<jms:listener-container factory-id="myContainerFactory" 
               connection-factory="connectionFactory"
               destination-resolver="destinationResolver"
               concurrency="3-10"/>

由于单个容器工厂设置非常常见,如果已设置或发现默认工厂,则可以省略 containerFactory 属性。默认情况下,我们会查找名为 jmsListenerContainerFactory 的 bean。

通过实现 JmsListenerConfigurer 接口,可以通过多种方式自定义此基础设施的配置。正如我们刚才提到的,可以显式指定要使用的默认容器工厂,但此回调接口也允许您以编程方式注册 JMS 端点!

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setDefaultContainerFactory(defaultContainerFactory());

        SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
        endpoint.setDestination("anotherQueue");
        endpoint.setMessageListener(message -> {
            // processing
        });
        registrar.registerEndpoint(endpoint);
    }

    @Bean
    public DefaultJmsListenerContainerFactory defaultContainerFactory() {
        ...
    }

上面的示例设置了默认的 JmsListenerContainerFactory,并在 anotherQueue 上配置了一个额外的端点。JmsListenerEndpoint 对您的端点进行建模,并负责为该模型配置容器。在上面的示例中,我们使用了 SimpleJmsListenerEndpoint,它提供了实际要调用的 MessageListener,但您也可以构建自己的端点变体来描述自定义调用机制。MethodJmsListenerEndpoint 是另一个示例,所有使用 @JmsListener 注解的端点都使用它。

消息抽象

到目前为止,我们一直在端点中注入一个简单的 String,但实际上它可以拥有非常灵活的方法签名。让我们重写它以注入带有自定义头部的 Order

@Component
public class MyService {

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

这些是您可以在 JMS 监听器端点中注入的主要元素

  • 原始的 javax.jms.Message 或其任何子类(当然前提是它与接收到的消息类型匹配)。
  • javax.jms.Session,用于可选访问原生的 JMS API,例如发送自定义回复。
  • 表示接收到的 JMS 消息的 org.springframework.messaging.Message。请注意,此消息包含自定义头和标准头(由 JmsHeaders 定义)。
  • 使用 @Header 注解的方法参数,用于提取特定的头部值,包括标准 JMS 头部。
  • 使用 @Headers 注解的参数,该参数也必须可以赋值给 java.util.Map 以获取所有头部。
  • 未注解的元素,如果不是支持的类型之一(即 MessageSession),则被视为有效载荷(payload)。您可以通过使用 @Payload 注解参数来明确这一点。您还可以通过添加额外的 @Validated 来开启验证。

注入 Spring 的 Message 抽象的能力特别有用,可以从传输特定消息中存储的所有信息中获益,而无需依赖于传输特定的 API。

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

这些功能在内部为所有注解元素提供支持。可以自定义验证和转换服务,甚至为您的自定义用例添加额外的方法参数解析器。以下示例设置了一个自定义 Validator,以便带有 @Validated 注解的载荷在调用监听器方法之前首先使用它进行验证

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

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

    @Bean
    public DefaultJmsHandlerMethodFactory myJmsHandlerMethodFactory() {
        DefaultJmsHandlerMethodFactory factory = new DefaultJmsHandlerMethodFactory();
        factory.setValidator(myValidator());
        return factory;
    }
}

回复管理

MessageListenerAdapter 中现有的支持已允许您的方法拥有非 void 返回类型。在这种情况下,调用结果会被封装在一个 javax.jms.Message 中发送,发送到原始消息的 JMSReplyTo 头部中指定的目标,或者发送到监听器上配置的默认目标。现在可以使用消息抽象的 @SendTo 注解来设置该默认目标。

假设我们的 processOrder 方法现在应返回一个 OrderStatus,可以如下编写以自动发送回复

@JmsListener(destination = "myQueue")
@SendTo("queueOut")
public OrderStatus processOrder(Order order) {
    // order processing
    return status;
}

如果需要以传输独立的方式设置额外的头部,您可以返回一个 Message,如下所示

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

总结

Spring Framework 4.1 定于今年七月发布,并在 JMS 领域包含了多项改进:JMS 监听器方法可以简单地使用注解,并且可以使用非常灵活的方法签名。Spring 4.0 中引入的消息抽象现在也支持 JMS 监听器了。

获取 Spring 新闻稿

订阅 Spring 新闻稿以保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区即将举行的所有活动。

查看全部