最新の安定バージョンについては、spring-cloud-stream 5.0.0 を使用してください。 |
力学
コンテンツ型のネゴシエーションの背後にあるメカニズムと必要性をよりよく理解するために、例として次のメッセージハンドラーを使用して、非常に単純なユースケースを見ていきます。
public Function<Person, String> personFunction {..}| 簡単にするために、これがアプリケーション内の唯一のハンドラー関数であると想定します(内部パイプラインがないと想定します)。 |
前の例に示されているハンドラーは、引数として Person オブジェクトを予期し、出力として String 型を生成します。フレームワークが受信 Message を引数としてこのハンドラーに渡すことに成功するには、Message 型のペイロードをワイヤー形式から Person 型に何らかの方法で変換する必要があります。つまり、フレームワークは適切な MessageConverter を見つけて適用する必要があります。これを実現するには、フレームワークにユーザーからの指示が必要です。これらの命令の 1 つは、ハンドラーメソッド自体のシグニチャー(Person 型)によってすでに提供されています。理論的には、それで十分なはずです(場合によってはそれで十分です)。ただし、ほとんどのユースケースでは、適切な MessageConverter を選択するために、フレームワークに追加の情報が必要です。その欠落している部分は contentType です。
Spring Cloud Stream は、contentType を(優先順に)定義するための 3 つのメカニズムを提供します。
HEADER :
contentTypeは、メッセージ自体を介して通信できます。contentTypeヘッダーを提供することにより、適切なMessageConverterを見つけて適用するために使用するコンテンツ型を宣言します。BINDING :
contentTypeは、spring.cloud.stream.bindings.input.content-typeプロパティを設定することにより、宛先バインディングごとに設定できます。プロパティ名の inputセグメントは、宛先の実際の名前(この場合は「入力」)に対応します。このアプローチでは、バインディングごとに、適切なMessageConverterを見つけて適用するために使用するコンテンツ型を宣言できます。DEFAULT :
contentTypeがMessageヘッダーまたはバインディングに存在しない場合、デフォルトのapplication/jsonコンテンツ型を使用して、適切なMessageConverterを見つけて適用します。
前述のように、上記のリストは、同点の場合の優先順位も示しています。例: ヘッダーで提供されるコンテンツ型は、他のどのコンテンツ型よりも優先されます。同じことがバインディングごとに設定されたコンテンツ型にも当てはまります。これにより、基本的にデフォルトのコンテンツ型を上書きできます。ただし、実用的なデフォルトも提供します(これはコミュニティのフィードバックから決定されました)。
application/json をデフォルトにする別の理由は、プロデューサーとコンシューマーが異なる JVM で実行されるだけでなく、異なる非 JVM プラットフォームでも実行できる分散マイクロサービスアーキテクチャによって駆動される相互運用性要件に起因します。
非 void ハンドラーメソッドが返されるときに、戻り値がすでに Message である場合、その Message がペイロードになります。ただし、戻り値が Message でない場合、新しい Message は、入力 Message からヘッダーを継承し、SpringIntegrationProperties.messageHandlerNotPropagatedHeaders によって定義またはフィルタリングされたヘッダーを差し引いたものをペイロードとして、戻り値を使用して作成されます。デフォルトでは、そこに設定されているヘッダーは contentType の 1 つだけです。これは、新しい Message に contentType ヘッダーが設定されていないため、contentType を確実に進化させることができることを意味します。ハンドラーメソッドから Message を返すことをいつでもオプトアウトできます。ここで、任意のヘッダーを挿入できます。
内部パイプラインがある場合、Message は、同じ変換プロセスを経て次のハンドラーに送信されます。ただし、内部パイプラインがない場合、パイプラインの最後に到達した場合、Message は出力先に送り返されます。
コンテンツ型と引数型
前述のように、フレームワークが適切な MessageConverter を選択するには、引数の型と、オプションでコンテンツ型の情報が必要です。適切な MessageConverter を選択するロジックは、引数リゾルバー (HandlerMethodArgumentResolvers) にあります。引数リゾルバーは、ユーザー定義のハンドラーメソッドの呼び出しの直前 (フレームワークが実際の引数の型を認識しているとき) にトリガーされます。引数の型が現在のペイロードの型と一致しない場合、フレームワークは、事前構成された MessageConverters のスタックに委譲して、そのいずれかがペイロードを変換できるかどうかを確認します。ご覧のとおり、MessageConverter の Object fromMessage(Message<?> message, Class<?> targetClass); 操作は、引数の 1 つとして targetClass を受け取ります。また、フレームワークは、提供された Message に常に contentType ヘッダーが含まれるようにします。contentType ヘッダーがまだ存在しない場合は、バインディングごとの contentType ヘッダーまたは既定の contentType ヘッダーのいずれかを挿入します。contentType 引数型の組み合わせは、フレームワークがメッセージをターゲット型に変換できるかどうかを判断するメカニズムです。適切な MessageConverter が見つからない場合は例外がスローされますが、カスタム MessageConverter (User-defined Message Converters を参照) を追加することでこれを処理できます。
しかし、ペイロード型がハンドラーメソッドによって宣言されたターゲット型と一致する場合はどうなるでしょうか? この場合、変換するものはなく、ペイロードは変更されずに渡されます。これは非常に単純で論理的に聞こえますが、Message<?> または Object を引数として取るハンドラーメソッドを覚えておいてください。ターゲット型を Object (Java ではすべて instanceof)であると宣言することにより、変換プロセスは本質的に失われます。
Message が contentType のみに基づいて他の型に変換されることを期待しないでください。contentType はターゲット型を補完することを忘れないでください。必要に応じて、MessageConverter が考慮に入れる場合と考慮しない場合があるヒントを提供できます。 |
メッセージコンバーター
MessageConverters は、次の 2 つのメソッドを定義します。
Object fromMessage(Message<?> message, Class<?> targetClass);
Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);特に Spring Cloud Stream のコンテキストでは、これらのメソッドの契約とその使用箇所を理解することが重要です。
fromMessage メソッドは、入力された Message を引数の型に変換します。Message のペイロードは任意の型でよく、複数の型をサポートするかどうかは MessageConverter の実際の実装次第です。例: ある JSON コンバーターはペイロードの型を byte[], String, その他としてサポートするかもしれません。これは、アプリケーションが内部パイプライン (入力 → handler1 → handler2 → ... → 出力) を持ち、上流のハンドラーの出力が Message であり、最初のワイヤーフォーマットでない可能性がある場合に重要です。
ただし、toMessage メソッドにはより厳密な契約があり、常に Message をワイヤ形式 byte[] に変換する必要があります。
すべての目的と目的で(特に、独自のコンバーターを実装する場合)、2 つのメソッドは次のシグネチャーを持っていると見なします。
Object fromMessage(Message<?> message, Class<?> targetClass);
Message<byte[]> toMessage(Object payload, @Nullable MessageHeaders headers);