DSL の基本

org.springframework.integration.dsl パッケージには、前述の IntegrationFlowBuilder API と多くの IntegrationComponentSpec 実装が含まれています。これらは、ビルダーでもあり、具体的なエンドポイントを構成するための流れるような API を提供します。IntegrationFlowBuilder インフラストラクチャは、チャネル、エンドポイント、ポーラー、チャネルインターセプターなどのメッセージベースのアプリケーションに共通のエンタープライズ統合パターン (英語) (EIP)を提供します。

IMPORTANT

IntegrationComponentSpec は FactoryBean 実装であるため、その getObject() メソッドを Bean 定義から呼び出すことはできません。IntegrationComponentSpec 実装は、Bean 定義に対してそのままにしておく必要があり、フレームワークがそのライフサイクルを管理します。ターゲット IntegrationComponentSpec 型 (FactoryBean 値) の Bean メソッドパラメーター注入は、Bean メソッド参照の代わりに IntegrationFlow Bean 定義に使用する必要があります。

エンドポイントは、読みやすくするために DSL で動詞として表されます。次のリストには、一般的な DSL メソッド名と関連する EIP エンドポイントが含まれています。

  • 変換→ Transformer

  • フィルター→ Filter

  • ハンドル→ ServiceActivator

  • 分割→ Splitter

  • 集約→ Aggregator

  • ルート→ Router

  • ブリッジ→ Bridge

概念的には、統合プロセスは、これらのエンドポイントを 1 つ以上のメッセージフローに構成することによって構築されます。EIP は「メッセージフロー」という用語を正式に定義していませんが、よく知られているメッセージングパターンを使用する作業単位と考えると便利です。DSL はチャネルとチャネル間のエンドポイントの構成を定義する IntegrationFlow コンポーネントを提供しますが、現在、IntegrationFlow はアプリケーションコンテキストで実際の Bean を設定する構成のロールのみを果たし、実行時には使用されません。ただし、IntegrationFlow の Bean を Lifecycle としてオートワイヤーして、この IntegrationFlow に関連付けられたすべての Spring Integration コンポーネントに委譲されるフロー全体の start() および stop() を制御できます。次の例では、IntegrationFlow fluent API を使用して、IntegrationFlowBuilder の EIP メソッドを使用して IntegrationFlow Bean を定義します。

@Bean
public IntegrationFlow integerFlow() {
    return IntegrationFlow.from("input")
            .<String, Integer>transform(Integer::parseInt)
            .get();
}

transform メソッドは、ラムダをエンドポイント引数として受け入れ、メッセージペイロードを操作します。このメソッドの実際の引数は GenericTransformer<S, T> インスタンスです。提供されているトランスフォーマー (ObjectToJsonTransformerFileToStringTransformer、その他) のいずれかをここで使用できます。

内部では、IntegrationFlowBuilder は MessageHandler とそのエンドポイントを、それぞれ MessageTransformingHandler と ConsumerEndpointFactoryBean で認識します。別の例を考えてみましょう:

@Bean
public IntegrationFlow myFlow() {
    return IntegrationFlow.from("input")
                .filter("World"::equals)
                .transform("Hello "::concat)
                .handle(System.out::println)
                .get();
}

上記の例は、Filter → Transformer → Service Activator のシーケンスを構成します。フローは " 'one way'" です。つまり、応答メッセージは提供せず、ペイロードを STDOUT に出力するだけです。エンドポイントは、直接チャネルを使用して自動的に相互接続されます。

ラムダと Message<?> 引数

EIP メソッドでラムダを使用する場合、「入力」引数は通常、メッセージペイロードです。メッセージ全体にアクセスする場合は、Class<?> を最初のパラメーターとして受け取るオーバーロードメソッドのいずれかを使用します。例: これは機能しません:

.<Message<?>, Foo>transform(m -> newFooFromMessage(m))

ラムダは引数型を保持せず、フレームワークはペイロードを Message<?> にキャストしようとするため、これは実行時に ClassCastException で失敗します。

代わりに、次を使用します。

.(Message.class, m -> newFooFromMessage(m))
Bean 定義のオーバーライド

Java DSL は、フロー定義でインラインで定義されたオブジェクトの Bean を登録できるだけでなく、既存の注入された Bean を再利用できます。インラインオブジェクトに定義された同じ Bean 名と既存の Bean 定義の場合、そのような構成が間違っていることを示す BeanDefinitionOverrideException がスローされます。ただし、prototype Bean を扱う場合、BeanFactory から prototype Bean を呼び出すたびに新しいインスタンスを取得するため、統合フロープロセッサーから既存の Bean 定義を検出する方法はありません。このようにして、提供されたインスタンスは、Bean 登録や既存の prototype Bean 定義に対する可能なチェックなしでそのまま IntegrationFlow で使用されます。ただし、明示的な id があり、この名前の Bean 定義が prototype スコープ内にある場合、このオブジェクトに対して BeanFactory.initializeBean() が呼び出されます。