訊息對映規則和約定

Spring Integration 提供了一種靈活的機制,可以在不提供額外配置的情況下,根據一些預設規則和特定約定,將訊息對映到方法及其引數。以下各節中的示例闡述了這些規則。

示例場景

以下示例展示了一個未加註解的單個引數(物件或原始型別),它不是 `Map` 或 `Properties` 物件,且返回型別非 void

public String doSomething(Object o);

輸入引數是一個訊息載荷。如果引數型別與訊息載荷不相容,將嘗試使用 Spring 3.0 提供的轉換服務進行轉換。返回值將作為返回訊息的載荷。

以下示例展示了一個未加註解的單個引數(物件或原始型別),它不是 `Map` 或 `Properties` 物件,且返回型別為 `Message`

public Message doSomething(Object o);

輸入引數是一個訊息載荷。如果引數型別與訊息載荷不相容,將嘗試使用 Spring 3.0 提供的轉換服務進行轉換。返回值是新構建的訊息,將傳送到下一個目的地。

以下示例展示了一個引數為訊息(或其子類)且返回型別為任意物件或原始型別的示例

public int doSomething(Message msg);

輸入引數本身就是一個 `Message`。返回值將成為傳送到下一個目的地的 `Message` 的載荷。

以下示例展示了一個引數為 `Message`(或其子類)且返回型別為 `Message`(或其子類)的示例

public Message doSomething(Message msg);

輸入引數本身就是一個 `Message`。返回值是新構建的 `Message`,將傳送到下一個目的地。

以下示例展示了一個引數型別為 `Map` 或 `Properties` 且返回型別為 `Message` 的示例

public Message doSomething(Map m);

這個場景有點特殊。雖然乍一看似乎很容易直接對映到訊息頭,但總是優先對映到 `Message` 載荷。這意味著如果 `Message` 載荷是 `Map` 型別,則此輸入引數代表 `Message` 載荷。然而,如果 `Message` 載荷不是 `Map` 型別,轉換服務不會嘗試轉換載荷,並且輸入引數將對映到訊息頭。

以下示例展示了兩個引數,其中一個引數是任意型別(物件或原始型別),但不是 `Map` 或 `Properties` 物件,另一個引數是 `Map` 或 `Properties` 型別(無論返回型別如何)

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

這種組合包含兩個輸入引數,其中一個引數是 `Map` 型別。非 `Map` 型別的引數(無論順序如何)對映到 `Message` 載荷,而 `Map` 或 `Properties` 型別的引數(無論順序如何)對映到訊息頭,這提供了一種很好的 POJO 方式來與 `Message` 結構互動。

以下示例展示了沒有引數(無論返回型別如何)的示例

public String doSomething();

此訊息處理器方法是根據傳送到此處理器連線的輸入通道的訊息呼叫的。但是,沒有對映 `Message` 資料,因此 `Message` 充當了觸發器來呼叫處理器。輸出按照 前面描述的規則 進行對映。

以下示例展示了沒有引數且返回型別為 void 的示例

public void soSomething();

此示例與前一個示例相同,但不產生任何輸出。

基於註解的對映

基於註解的對映是將訊息對映到方法的最安全且歧義最小的方法。以下示例展示瞭如何將方法顯式對映到頭

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

正如您稍後可以看到的,如果沒有註解,此簽名將導致歧義。然而,透過將第一個引數顯式對映到 `Message` 載荷,將第二個引數顯式對映到 `someheader` 訊息頭的值,我們避免了任何歧義。

以下示例與前一個示例幾乎相同

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

`@RequestMapping` 或任何其他非 Spring Integration 對映註解是無關緊要的,因此會被忽略,導致第二個引數未被對映。雖然第二個引數很容易對映到載荷,但載荷只能有一個。因此,註解使此方法避免了歧義。

以下示例展示了另一個類似的方法,如果沒有註解來澄清意圖,它將是模糊的

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

唯一的區別在於第一個引數被隱式對映到訊息載荷。

以下示例展示了另一個簽名,如果沒有註解,肯定會被視為模糊,因為它有多個引數

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

這個示例會特別有問題,因為它的兩個引數都是 `Map` 例項。然而,透過基於註解的對映,可以輕鬆避免歧義。在此示例中,第一個引數對映到所有訊息頭,而第二個和第三個引數對映到名為 'something' 和 'someotherthing' 的訊息頭的值。載荷未對映到任何引數。

複雜場景

以下示例使用多個引數

多個引數在確定適當的對映時可能會產生很多歧義。一般的建議是使用 `@Payload`、`@Header` 和 `@Headers` 註解您的方法引數。本節中的示例展示了導致丟擲異常的模糊條件。

public String doSomething(String s, int i)

這兩個引數權重相等。因此,無法確定哪個是載荷。

以下示例展示了一個類似的問題,但有三個引數

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

雖然 `Map` 可以很容易地對映到訊息頭,但無法確定如何處理這兩個 String 引數。

以下示例展示了另一個模糊的方法

public String foo(Map m, Map f)

雖然有人可能會爭辯說一個 `Map` 可以對映到訊息載荷,另一個對映到訊息頭,但我們不能依賴順序。

任何包含多於一個未加註解的方法引數,並且不符合預設規則可唯一確定對映條件的簽名,都會導致模糊條件並觸發異常。

下一組示例分別展示了導致模糊的多個方法。

具有多個方法的訊息處理器根據前面(在示例中)描述的相同規則進行對映。然而,某些場景可能仍然看起來令人困惑。

以下示例展示了具有合法(可對映且無歧義)簽名的多個方法

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

    public String doSomething(Map m);
}

(無論方法名稱相同還是不同,都沒有區別)。`Message` 可以對映到任一方法。當訊息載荷可以對映到 `str` 且訊息頭可以對映到 `m` 時,將呼叫第一個方法。僅透過將訊息頭對映到 `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` 屬性來影響對映(如下一個示例所示)。以下示例展示了具有兩個不同方法名稱的相同示例

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` 方法,我們消除了歧義。