返信管理
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/thing2
:replyTo
交換とroutingKey
。thing1/
:replyTo
交換とデフォルト (空の)routingKey
。thing2
または/thing2
:replyTo
routingKey
とデフォルト (空の) 交換。/
または空: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 つのプロパティがあります。
request
:o.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 ...
}