Spring 4.3 JMS関連の主な変更点

今回は、Spring Framework 4.3の変更点紹介シリーズの第4回で、JMS関連の変更点を紹介します。

# シリーズ

* 第１回：[Spring 4.3 DIコンテナ関連の主な変更点](http://qiita.com/kazuki43zoo/items/172d132ff8f4ba098888)
* 第２回：[Spring 4.3 データアクセス関連の主な変更点](http://qiita.com/kazuki43zoo/items/bc036b433444f5c33dc4)
* 第３回：[Spring 4.3 キャッシュ関連の主な変更点](http://qiita.com/kazuki43zoo/items/43cea4b36822b58c0562)
* 第５回：[Spring 4.3 Web関連の主な変更点](http://qiita.com/kazuki43zoo/items/53d2ca25c174ff618331)
* 第６回：[Spring 4.3 WebSocket関連の主な変更点](http://qiita.com/kazuki43zoo/items/f2eac11f94e2afc7c964)
* 第７回(最終回)：[Spring 4.3 テスト関連の主な変更点](http://qiita.com/kazuki43zoo/items/6b6ecafd8e69a54e4803)

# 動作検証環境

* Spring Framework 4.3.0.RELEASE
* Spring Boot 1.4.0.BUILD-SNAPSHOT (2016/6/11時点)
* Apache ActiveMQ 5.13.3

# JMS Improvements

今回は、JMS関連の主な変更点をみていきます。公式リファレンスを見る限りでは、機能追加はないようです。

| No | JMS関連の主な変更点 |
| --- | --- |
| 1 | `@SendTo`をクラスレベルに付与することができるようになります。 |
| 2 | `@JmsListener`と`@JmsListeners`をメタアノテーションとして利用できるようになります。 |

# `@SendTo`をクラスレベルに付与することができる :+1: 

Spring 4.3から、`@SendTo`をクラスレベルに付与することで、応答メッセージの送信先を各メソッドで共有することができます。

```java:リスナー側
@Component
@SendTo("testReplyQueue") // クラスレベルに指定して、応答先を各メソッドで共有！！
public class JmsTestListeners {

    private static final Logger logger = LoggerFactory.getLogger(JmsTestListeners.class);

    @JmsListener(destination = "testQueue", selector = "JMSPriority = 9", concurrency = "1-50")
    public String receiveHighQuality(String text) {
        logger.info(text);
        return "HighQuality:" + UUID.randomUUID().toString();
    }

    @JmsListener(destination = "testQueue", selector = "JMSPriority < 9", concurrency = "1")
    public String receiveOtherQuality(String text) {
        logger.info(text);
        return "OtherQuality:" + UUID.randomUUID().toString();
    }
}
```

```java:クライアント側
@Autowired
ConnectionFactory connectionFactory;

@Test
public void send() throws InterruptedException, JMSException {
    JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
    jmsTemplate.setExplicitQosEnabled(true);
    jmsTemplate.setPriority(9);

    jmsTemplate.send("testQueue", (s) -> s.createTextMessage("high"));
    TextMessage message = (TextMessage) jmsTemplate.receive("testReplyQueue");
    logger.info(message.getText());

    jmsTemplate.setPriority(4);
    jmsTemplate.send("testQueue", (s) -> s.createTextMessage("low"));
    message = (TextMessage) jmsTemplate.receive("testReplyQueue");
    logger.info(message.getText());
}
```

ちなみに・・・Spring Bootから提供されている`spring-boot-starter-activemq`を利用すれば、特別なBean定義を行わなくても組み込みActive MQを使用したJMSを利用することができます。

```xml:pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
```

# `@JmsListener`と`@JmsListeners`をメタアノテーションとして利用できる :+1: 

Spring 4.3から、`@JmsListener`と`@JmsListeners`をメタアノテーションとして利用できるようになります。
どういう切り口でカスタムアノテーションを作るかは自由ですが、ここでは、優先度が高いメッセージのみを扱うリスナーであることを表現するカスタムアノテーションを使ってみます。

```java:カスタムアノテーション
package com.example;

import org.springframework.core.annotation.AliasFor;
import org.springframework.jms.annotation.JmsListener;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@JmsListener(destination = "", selector = "JMSPriority = 9", concurrency = "1-50")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HighQualityJmsListener {

    @AliasFor(annotation = JmsListener.class)
    String destination();

}
```

```java:リスナーメソッド
// @JmsListener(destination = "", selector = "JMSPriority = 9", concurrency = "1-50")
@HighQualityJmsListener(destination = "testQueue") // カスタムアノテーションを利用
public String receiveHighQuality(String text) {
    logger.info(text);
    return "HighQuality:" + UUID.randomUUID().toString();
}
```

# まとめ

今回は、JMS関連の主な変更点を紹介しました。大きな変更はないようですが、同じような定義を集約できる仕組みの導入はありがたいと思います。次回は、いよいよ「Web関連の主な変更点」を紹介する予定です。

# 参考サイト

* http://docs.spring.io/spring/docs/4.3.0.RELEASE/spring-framework-reference/htmlsingle/