メッセージハンドラーチェーン

MessageHandlerChain は MessageHandler の実装であり、フィルター、トランスフォーマー、スプリッターなどの他のハンドラーのチェーンに実際に委譲しながら、単一のメッセージエンドポイントとして構成できます。複数のハンドラーを固定された線形進行で接続する必要がある場合、これにより構成がより単純になります。例: 他のコンポーネントの前にトランスを提供することはかなり一般的です。同様に、チェーンの他のコンポーネントの前にフィルターを提供する場合、基本的に選択的コンシューマー (英語) を作成します。いずれの場合も、チェーンは単一の input-channel と単一の output-channel のみを必要とし、個々のコンポーネントごとにチャネルを定義する必要がなくなります。

MessageHandlerChain は、主に XML 構成用に設計されています。Java DSL の場合、IntegrationFlow 定義はチェーンコンポーネントとして扱うことができますが、以下のこの章で説明する概念や原則とは何の関係もありません。詳細については、Java DSL を参照してください。
Spring Integration の Filter は、ブールプロパティ throwExceptionOnRejection を提供します。同じポイントツーポイントチャネルで複数の選択的コンシューマーを異なる受け入れ条件で提供する場合、この値を "true" に設定する必要があります(デフォルトは false)。これにより、ディスパッチャーはメッセージが拒否されたことを認識し、その結果、メッセージを他のサブスクライバーに渡そうとします。例外がスローされなかった場合、ディスパッチャーには、それ以上の処理を防ぐためにフィルターがメッセージをドロップしたにもかかわらず、メッセージが正常に渡されたように見えます。実際にメッセージを「ドロップ」したい場合は、フィルターの "discard-channel" が役立つ場合があります。これは、ドロップされたメッセージに対して何らかの操作(JMS キューへの送信や書き込みなど)を実行する機会を与えるためです。ログへ)。

ハンドラーチェーンは、コンポーネント間の同程度の疎結合を内部的に維持しながら構成を簡素化します。ある時点で非線形配置が必要な場合、構成を変更するのは簡単です。

内部的には、チェーンは匿名チャネルで区切られたリストされたエンドポイントの線形セットアップに拡張されます。応答チャネルヘッダーは、チェーン内では考慮されません。最後のハンドラーが呼び出された後にのみ、結果のメッセージが応答チャネルまたはチェーンの出力チャネルに転送されます。このセットアップのため、最後を除くすべてのハンドラーは MessageProducer インターフェース('setOutputChannel()' メソッドを提供)を実装する必要があります。MessageHandlerChain の outputChannel が設定されている場合、最後のハンドラーには出力チャンネルのみが必要です。

他のエンドポイントと同様に、output-channel はオプションです。チェーンの最後に応答メッセージがある場合、出力チャネルが優先されます。ただし、使用できない場合、チェーンハンドラーは、フォールバックとして受信メッセージの応答チャネルヘッダーをチェックします。

ほとんどの場合、MessageHandler を自分で実装する必要はありません。次のセクションでは、チェーン要素のネームスペースサポートに焦点を当てます。サービスアクティベーターやトランスフォーマーなど、ほとんどの Spring Integration エンドポイントは、MessageHandlerChain 内での使用に適しています。

チェーンの構成

<chain> 要素は、input-channel 属性を提供します。チェーンの最後の要素が応答メッセージを生成できる場合(オプション)、output-channel 属性もサポートします。サブ要素は、フィルター、トランスフォーマー、スプリッター、サービスアクティベーターです。最後の要素は、ルーターまたは送信チャネルアダプターの場合もあります。次の例は、チェーン定義を示しています。

<int:chain input-channel="input" output-channel="output">
    <int:filter ref="someSelector" throw-exception-on-rejection="true"/>
    <int:header-enricher>
        <int:header name="thing1" value="thing2"/>
    </int:header-enricher>
    <int:service-activator ref="someService" method="someMethod"/>
</int:chain>

上記の例で使用されている <header-enricher> 要素は、thing1 という名前のメッセージヘッダーにメッセージの値 thing2 を設定します。ヘッダーエンリッチャーは、ヘッダー値のみに触れる Transformer の特殊化です。ヘッダーを変更する MessageHandler を実装し、それを Bean として接続することで同じ結果を得ることができますが、ヘッダーエンリッチャーはより簡単なオプションです。

<chain> は、メッセージフローの最後の「クローズドボックス」コンシューマーとして構成できます。このソリューションでは、次の例に示すように、<chain> の最後に <outbound-channel-adapter> を配置できます。

<int:chain input-channel="input">
    <int-xml:marshalling-transformer marshaller="marshaller" result-type="StringResult" />
    <int:service-activator ref="someService" method="someMethod"/>
    <int:header-enricher>
        <int:header name="thing1" value="thing2"/>
    </int:header-enricher>
    <int:logging-channel-adapter level="INFO" log-full-message="true"/>
</int:chain>
許可されていない属性と要素

order や input-channel などの特定の属性は、チェーン内で使用されるコンポーネントで指定することはできません。同じことが poller サブ要素にも当てはまります。

Spring Integration コアコンポーネントの場合、XML スキーマ自体がこれらの制約の一部を実施します。ただし、非コアコンポーネントまたは独自のカスタムコンポーネントの場合、これらの制約は XML スキーマではなく、XML 名前空間パーサーによって適用されます。

これらの XML 名前空間パーサー制約は Spring Integration 2.2 で追加されました。許可されていない属性と要素を使用しようとすると、XML 名前空間パーサーは BeanDefinitionParsingException をスローします。

"id" 属性の使用

Spring Integration 3.0 以降、チェーン要素に id 属性が指定されている場合、要素の Bean 名は、チェーンの id と要素自体の id の組み合わせです。id 属性のない要素は Bean として登録されませんが、それぞれにチェーン id を含む componentName が与えられます。次の例について考えてみます。

<int:chain id="somethingChain" input-channel="input">
    <int:service-activator id="somethingService" ref="someService" method="someMethod"/>
    <int:object-to-json-transformer/>
</int:chain>

前の例では:

  • <chain> ルート要素には、"somethingChain" の id があります。AbstractEndpoint 実装(input-channel 型に応じて PollingConsumer または EventDrivenConsumer)Bean は、この値を Bean 名として使用します。

  • MessageHandlerChain Bean は Bean エイリアス('somethingChain.handler' )を取得します。これにより、BeanFactory からこの Bean に直接アクセスできます。

  • <service-activator> は完全なメッセージングエンドポイントではありません(PollingConsumer または EventDrivenConsumer ではありません)。<chain> 内の MessageHandler です。この場合、BeanFactory に登録されている Bean 名は "somethingChain$child.somethingService.handler" です。

  • この ServiceActivatingHandler の componentName は同じ値を取りますが、接尾辞 ".handler" はありません。"somethingChain$child.somethingService" になります。

  • 最後の <chain> サブコンポーネント <object-to-json-transformer> には、id 属性がありません。その componentName は <chain> での位置に基づいています。この場合、"somethingChain$child#1" です。(名前の最後の要素は、"#0" で始まる チェーン 内の順序です)。このトランスフォーマーは、アプリケーションコンテキスト内で Bean として登録されていないため、beanName を取得しないことに注意してください。ただし、その componentName には、ロギングやその他の目的に役立つ値があります。

<chain> 要素の id 属性により、<chain> 要素は JMX エクスポートの対象となり、メッセージ履歴で追跡可能になります。前述したように、適切な Bean 名を使用して、BeanFactory からこれらにアクセスできます。

<chain> 要素に明示的な id 属性を提供して、ログ内のサブコンポーネントの識別を簡素化し、BeanFactory などからそれらへのアクセスを提供すると便利です。

チェーン内からチェーンを呼び出す

場合によっては、チェーン内から別のチェーンにネストされた呼び出しを行ってから、元のチェーン内で戻って実行を継続する必要があります。これを実現するには、次の例に示すように、<gateway> 要素を含めることでメッセージングゲートウェイを使用できます。

<int:chain id="main-chain" input-channel="in" output-channel="out">
    <int:header-enricher>
      <int:header name="name" value="Many" />
    </int:header-enricher>
    <int:service-activator>
      <bean class="org.foo.SampleService" />
    </int:service-activator>
    <int:gateway request-channel="inputA"/>
</int:chain>

<int:chain id="nested-chain-a" input-channel="inputA">
    <int:header-enricher>
        <int:header name="name" value="Moe" />
    </int:header-enricher>
    <int:gateway request-channel="inputB"/>
    <int:service-activator>
        <bean class="org.foo.SampleService" />
    </int:service-activator>
</int:chain>

<int:chain id="nested-chain-b" input-channel="inputB">
    <int:header-enricher>
        <int:header name="name" value="Jack" />
    </int:header-enricher>
    <int:service-activator>
        <bean class="org.foo.SampleService" />
    </int:service-activator>
</int:chain>

上記の例では、nested-chain-a は、そこに構成された 'gateway' 要素によって main-chain 処理の終わりに呼び出されます。nested-chain-a では、ヘッダーの強化後に nested-chain-b の呼び出しが行われます。その後、フローは戻って nested-chain-b で実行を終了します。最後に、フローは main-chain に戻ります。<gateway> 要素のネストされたバージョンがチェーンで定義されている場合、service-interface 属性は必要ありません。代わりに、現在の状態のメッセージを取得し、request-channel 属性で定義されたチャネルに配置します。そのゲートウェイによって開始されたダウンストリームフローが完了すると、Message がゲートウェイに返され、現在のチェーン内でその旅を続けます。