メッセージマッピングのルールと規則

Spring Integration は、いくつかのデフォルトルールに依存して特定の規則を定義することにより、追加の構成を提供することなく、メッセージをメソッドとその引数にマッピングする柔軟な機能を実装しています。次のセクションの例は、ルールを明確にします。

サンプルシナリオ

次の例は、Map または非 void 戻り型の Properties オブジェクトではない単一のアノテーションなしパラメーター(オブジェクトまたはプリミティブ)を示しています。

public String doSomething(Object o);

入力パラメーターはメッセージのペイロードです。パラメーター型がメッセージペイロードと互換性がない場合は、Spring 3.0 が提供する変換サービスを使用してそれを変換しようとします。戻り値は、返されたメッセージのペイロードとして組み込まれます。

次の例は、Map または Message 戻り値の型の Properties ではない単一のアノテーションなしパラメーター(オブジェクトまたはプリミティブ)を示しています。

public Message doSomething(Object o);

入力パラメーターはメッセージのペイロードです。パラメーター型がメッセージペイロードと互換性がない場合は、Spring 3.0 が提供する変換サービスを使用してそれを変換しようとします。戻り値は、次の宛先に送信される新しく作成されたメッセージです。

次の例は、任意のオブジェクトまたはプリミティブの戻り型を持つメッセージ (またはそのサブクラスの 1 つ) である単一のパラメーターを示しています。

public int doSomething(Message msg);

入力パラメーター自体は Message です。戻り値は、次の宛先に送信される Message のペイロードになります。

次の例は、Message (またはそのサブクラスの 1 つ)を戻り型として持つ Message (またはそのサブクラスの 1 つ)である単一のパラメーターを示しています。

public Message doSomething(Message msg);

入力パラメーター自体は Message です。戻り値は、新しく構築された Message であり、次の宛先に送信されます。

次の例は、Map または Properties の単一のパラメーターと戻り値の型として Message を示しています。

public Message doSomething(Map m);

これは少し興味深いです。最初は、メッセージヘッダーに直接簡単にマッピングできるように思われますが、常に Message ペイロードが優先されます。つまり、Message ペイロードの型が Map である場合、この入力引数は Message ペイロードを表します。ただし、Message ペイロードの型が Map でない場合、変換サービスはペイロードの変換を試行せず、入力引数はメッセージヘッダーにマップされます。

次の例は 2 つのパラメーターを示しています。1 つは Map または Properties オブジェクトではない任意の型(オブジェクトまたはプリミティブ)であり、もう 1 つは型 Map または Properties 型(戻り値に関係なく)です。

public Message doSomething(Map h, <T> t);

この組み合わせには、そのうちの 1 つが Map 型である 2 つの入力パラメーターが含まれます。Map 以外のパラメーター(順序に関係なく)は Message ペイロードにマッピングされ、Map または Properties (順序に関係なく)はメッセージヘッダーにマッピングされ、Message 構造と対話するための素晴らしい POJO 方法を提供します。

次の例は、パラメーターを示していません(戻り値に関係なく)。

public String doSomething();

このメッセージハンドラーメソッドは、このハンドラーが接続されている入力チャネルに送信された Message に基づいて呼び出されます。ただし、Message データはマップされないため、Message をイベントまたはトリガーとして機能させてハンドラーを呼び出します。出力は、前述のルールに従ってマップされます。

次の例は、パラメーターと void 戻り値を示しています。

public void soSomething();

この例は前の例と同じですが、出力は生成されません。

アノテーションベースのマッピング

アノテーションベースのマッピングは、メッセージをメソッドにマッピングするための最も安全で曖昧さの少ないアプローチです。次の例は、メソッドをヘッダーに明示的にマップする方法を示しています。

public String doSomething(@Payload String s, @Header("someheader") String b)

後で見ることができるように、アノテーションがない場合、このシグネチャーはあいまいな状態になります。ただし、最初の引数を Message ペイロードに明示的にマッピングし、2 番目の引数を someheader メッセージヘッダーの値に明示的にマッピングすることにより、あいまいさを回避します。

次の例は、前の例とほぼ同じです。

public String doSomething(@Payload String s, @RequestParam("something") String b)

@RequestMapping またはその他の Spring Integration 以外のマッピングアノテーションは無関係であるため無視され、2 番目のパラメーターはマッピングされません。2 番目のパラメーターは簡単にペイロードにマッピングできますが、ペイロードは 1 つしか存在できません。アノテーションにより、このメソッドがあいまいになりません。

次の例は、アノテーションが意図を明確にしないとあいまいになる別の同様の方法を示しています。

public String foo(String s, @Header("foo") String b)

唯一の違いは、最初の引数が暗黙的にメッセージペイロードにマップされることです。

次の例は、3 つ以上の引数があるため、アノテーションなしでは間違いなく処理される別の署名を示しています。

public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar)

引数の 2 つが Map インスタンスであるため、この例は特に問題になります。ただし、アノテーションベースのマッピングを使用すると、あいまいさを簡単に回避できます。この例では、最初の引数はすべてのメッセージヘッダーにマッピングされ、2 番目と 3 番目の引数は "something" および "someotherthing" という名前のメッセージヘッダーの値にマッピングされます。ペイロードはどの引数にもマップされていません。

複雑なシナリオ

次の例では、複数のパラメーターを使用しています。

複数のパラメーターを使用すると、適切なマッピングを決定する際に多くのあいまいさが生じる可能性があります。一般的なアドバイスは、メソッドパラメーターに @Payload@Header@Headers でアノテーションを付けることです。このセクションの例は、例外の発生につながるあいまいな条件を示しています。

public String doSomething(String s, int i)

2 つのパラメーターの重量は同じです。どれがペイロードであるかを判別する方法はありません。

次の例は、3 つのパラメーターのみがある同様の問題を示しています。

public String foo(String s, Map m, String b)

Map はメッセージヘッダーに簡単にマッピングできますが、2 つの String パラメーターをどうするかを決定する方法はありません。

次の例は、別のあいまいな方法を示しています。

public String foo(Map m, Map f)

1 つの Map をメッセージペイロードに、もう 1 つをメッセージヘッダーにマッピングできると主張するかもしれませんが、順序に依存することはできません。

(Map<T>) ではない複数のメソッド引数とアノテーションのないパラメーターを持つメソッドシグネチャーは、あいまいな状態になり、例外をトリガーします。

次の一連の例はそれぞれ、あいまいさをもたらす複数のメソッドを示しています。

複数のメソッドを持つメッセージハンドラーは、前の例で説明したのと同じルールに基づいてマップされます。ただし、一部のシナリオは依然として混乱を招く可能性があります。

次の例は、正当な(マッピング可能かつ明確な)署名を持つ複数のメソッドを示しています。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(Map m);
}

(メソッドの名前が同じでも、異なる名前でも違いはありません)。Message は、どちらの方法にもマッピングできます。最初のメソッドは、メッセージペイロードを str にマップでき、メッセージヘッダーを m にマップできるときに呼び出されます。2 番目の方法は、メッセージヘッダーのみを m にマッピングすることで候補にすることもできます。さらに悪いことに、両方のメソッドの名前は同じです。最初は、次の構成のためにあいまいに見える場合があります。

<int:service-activator input-channel="input" output-channel="output" method="doSomething">
    <bean class="org.things.Something"/>
</int:service-activator>

マッピングは最初にペイロードに基づいており、次に他のすべてに基づいているため機能します。つまり、最初の引数をペイロードにマッピングできるメソッドは、他のすべてのメソッドよりも優先されます。

次に、真に曖昧な状態を生成する別の例を考えてみましょう。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(String str);
}

どちらの方法にも、メッセージペイロードにマップできるシグネチャーがあります。彼らも同じ名前を持っています。このようなハンドラーメソッドは例外をトリガーします。ただし、メソッド名が異なる場合は、method 属性を使用してマッピングに影響を与えることができます(次の例に示す)。次の例は、2 つの異なるメソッド名を使用した同じ例を示しています。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomethingElse(String str);
}

次の例は、method 属性を使用してマッピングを指示する方法を示しています。

<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse">
    <bean class="org.bar.Foo"/>
</int:service-activator>

構成では doSomethingElse メソッドを明示的にマップするため、あいまいさはなくなりました。