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 框架的下一個主要版本將允許您透過簡單的註解來定義 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,則可以省略 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,例如傳送自定義回覆。
  • org.springframework.messaging.Message,代表傳入的 JMS 訊息。請注意,此訊息同時包含自定義頭和標準頭(由 JmsHeaders 定義)。
  • @Header 註解的方法引數,用於提取特定的頭值,包括標準 JMS 頭。
  • @Headers 註解的引數,它也必須可賦值給 java.util.Map,以便訪問所有頭。
  • 一個非註解的元素,它不是支援的型別之一(即 MessageSession),將被視為有效負載。您可以透過用 @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 預計於今年 7 月釋出,其中包括 JMS 領域的幾項改進:JMS 監聽器方法可以簡單地用註解來定義,並且可以使用非常靈活的方法簽名。Spring 4.0 中引入的訊息傳遞抽象現在也支援 JMS 監聽器。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有