訊息對映規則和約定
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(無論順序如何)對映到訊息頭,為您提供一種與 Message 結構互動的 POJO 方式。
以下示例展示了沒有引數(無論返回型別如何)
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)
此示例尤其 problematic,因為它的兩個引數是 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 可以對映到訊息負載,另一個對映到訊息頭,但我們不能依賴順序。
任何具有多個方法引數(不是 Map, <T>)且引數沒有註解的方法簽名都會導致歧義條件並觸發異常。 |
下一組示例都展示了導致歧義的多個方法。
具有多個方法的訊息處理程式根據前面描述的相同規則進行對映(在示例中)。但是,某些場景可能仍然看起來令人困惑。
以下示例展示了具有合法(可對映且明確)簽名的多個方法
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 方法,我們消除了歧義。