返信管理

MessageListenerAdapter の既存のサポートにより、メソッドはすでに void 以外の戻り値の型を持つことができます。この場合、呼び出しの結果は、元のメッセージの ReplyToAddress ヘッダーで指定されたアドレス、またはリスナーに構成されたデフォルトのアドレスに送信されるメッセージにカプセル化されます。メッセージング抽象化の @SendTo アノテーションを使用して、そのデフォルトのアドレスを設定できます。

processOrder メソッドが OrderStatus を返す必要があると仮定すると、次のように記述して、自動的に返信を送信できます。

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

トランスポートに依存しない方法で追加のヘッダーを設定する必要がある場合は、代わりに次のような Message を返すことができます。

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

または、beforeSendReplyMessagePostProcessors コンテナーファクトリプロパティで MessagePostProcessor を使用して、ヘッダーを追加することもできます。バージョン 2.2.3 から、呼び出された Bean/ メソッドが応答メッセージで使用できるようになりました。これをメッセージポストプロセッサーで使用して、呼び出し元に情報を返すことができます。

factory.setBeforeSendReplyPostProcessors(msg -> {
    msg.getMessageProperties().setHeader("calledBean",
            msg.getMessageProperties().getTargetBean().getClass().getSimpleName());
    msg.getMessageProperties().setHeader("calledMethod",
            msg.getMessageProperties().getTargetMethod().getName());
    return m;
});

バージョン 2.2.5 以降では、応答メッセージを送信前に変更するように ReplyPostProcessor を構成できます。リクエストに一致するように correlationId ヘッダーが設定された後に呼び出されます。

@RabbitListener(queues = "test.header", group = "testGroup", replyPostProcessor = "echoCustomHeader")
public String capitalizeWithHeader(String in) {
    return in.toUpperCase();
}

@Bean
public ReplyPostProcessor echoCustomHeader() {
    return (req, resp) -> {
        resp.getMessageProperties().setHeader("myHeader", req.getMessageProperties().getHeader("myHeader"));
        return resp;
    };
}

バージョン 3.0 以降では、アノテーションではなくコンテナーファクトリでポストプロセッサーを構成できます。

factory.setReplyPostProcessorProvider(id -> (req, resp) -> {
    resp.getMessageProperties().setHeader("myHeader", req.getMessageProperties().getHeader("myHeader"));
    return resp;
});

id パラメーターはリスナー ID です。

アノテーションの設定は、提供時の設定よりも優先されます。

@SendTo 値は、exchange/routingKey パターンに従う応答 exchange と routingKey のペアと見なされます。これらの部分の 1 つを省略できます。有効な値は次のとおりです。

  • thing1/thing2replyTo 交換と routingKeything1/replyTo 交換とデフォルト (空の) routingKeything2 または /thing2replyToroutingKey とデフォルト (空の) 交換。/ または空: replyTo デフォルト交換とデフォルト routingKey

また、value 属性なしで @SendTo を使用することもできます。この場合は、空の sendTo パターンと同じです。@SendTo は、受信メッセージに replyToAddress プロパティがない場合にのみ使用されます。

バージョン 1.5 以降では、次の例に示すように、@SendTo 値を Bean 初期化 SpEL 式にすることができます。

@RabbitListener(queues = "test.sendTo.spel")
@SendTo("#{spelReplyTo}")
public String capitalizeWithSendToSpel(String foo) {
    return foo.toUpperCase();
}
...
@Bean
public String spelReplyTo() {
    return "test.sendTo.reply.spel";
}

式は String に評価される必要があります。これは、前の例で説明したように、単純なキュー名 (デフォルトの交換に送信される) または exchange/routingKey の形式にすることができます。

#{…​} 式は、初期化中に 1 回評価されます。

動的な返信ルーティングの場合、メッセージ送信者は reply_to メッセージプロパティを含めるか、別のランタイム SpEL 式を使用する必要があります (次の例の後に説明します)。

バージョン 1.6 以降、@SendTo は、次の例に示すように、リクエストと応答に対して実行時に評価される SpEL 式にすることができます。

@RabbitListener(queues = "test.sendTo.spel")
@SendTo("!{'some.reply.queue.with.' + result.queueName}")
public Bar capitalizeWithSendToSpel(Foo foo) {
    return processTheFooAndReturnABar(foo);
}

SpEL 式の実行時の性質は、!{…​} 区切り文字で示されます。式の評価コンテキスト #root オブジェクトには、次の 3 つのプロパティがあります。

  • requesto.s.amqp.core.Message リクエストオブジェクト。

  • source: コンバート後の o.s.messaging.Message<?>

  • result: メソッドの結果。

コンテキストには、マッププロパティアクセサー、標準型コンバーター、コンテキスト内の他の Bean を参照できるようにする Bean リゾルバー (たとえば、@someBeanName.determineReplyQ(request, result)) があります。

要約すると、#{…​} は初期化中に 1 回評価され、#root オブジェクトがアプリケーションコンテキストになります。Bean はその名前で参照されます。!{…​} は、各メッセージの実行時に評価され、ルートオブジェクトは前にリストされたプロパティを持ちます。Bean は、@ で始まる名前で参照されます。

バージョン 2.1 以降では、単純なプロパティプレースホルダーもサポートされています (たとえば、${some.reply.to})。以前のバージョンでは、次の例に示すように、回避策として次の方法を使用できます。

@RabbitListener(queues = "foo")
@SendTo("#{environment['my.send.to']}")
public String listen(Message in) {
    ...
    return ...
}