訊息映射規則與慣例
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
可以映射到訊息酬載,另一個可以映射到訊息標頭,但我們不能依賴順序。
任何具有多個方法引數且不是 (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
方法,因此我們消除了歧義。