跳到主要内容

消息映射规则和约定

QWen Plus 中英对照 Message Mapping Rules and Conventions

Spring Integration 实现了一个灵活的设施,用于将消息映射到方法及其参数,而无需提供额外的配置,它依赖于一些默认规则并定义某些约定。以下各节中的示例阐述了这些规则。

示例场景

以下示例展示了一个单一未注解的参数(对象或基本类型),该参数不是 MapProperties 对象,并且具有非 void 返回类型:

public String doSomething(Object o);
java

输入参数是一个消息有效负载。如果参数类型与消息有效负载不兼容,则会尝试通过使用 Spring 3.0 提供的转换服务来转换它。返回值将作为返回消息的有效负载被包含。

以下示例展示了一个单一未注解的参数(对象或基本类型),它不是 MapProperties,并且返回类型为 Message

public Message doSomething(Object o);
java

输入参数是一个消息有效负载。如果参数类型与消息有效负载不兼容,则尝试通过使用 Spring 3.0 提供的转换服务来转换它。返回值是新构造的消息,该消息将发送到下一个目的地。

以下示例展示了一个单一参数,该参数是一个消息(或其一个子类),具有任意对象或基本返回类型:

public int doSomething(Message msg);
java

输入参数本身是一个 Message。返回值成为发送到下一个目的地的 Message 的有效负载。

以下示例显示了一个参数,该参数是 Message(或其一个子类),返回类型也是 Message(或其一个子类):

public Message doSomething(Message msg);
java

输入参数本身是一个 Message。返回值是一个新构造的 Message,它将被发送到下一个目的地。

以下示例展示了一个类型为 MapProperties 的单个参数,返回类型为 Message

public Message doSomething(Map m);
java

这一个有点有趣。虽然一开始看起来像是可以直接映射到消息头,但总是优先考虑 Message 负载。这意味着如果 Message 负载是 Map 类型,这个输入参数就代表一个 Message 负载。然而,如果 Message 负载不是 Map 类型,转换服务不会尝试转换负载,而输入参数会被映射到消息头。

以下示例显示了两个参数,其中一个参数是任意类型(对象或基本类型),但不是 MapProperties 对象,另一个参数是 MapProperties 类型(无论返回值如何):

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

这种组合包含两个输入参数,其中一个参数的类型为 Map。非 Map 参数(无论顺序如何)将映射到 Message 负载,而 MapProperties(无论顺序如何)将映射到消息头,从而为您提供一种简洁的 POJO 方式来与 Message 结构进行交互。

以下示例显示没有参数(无论返回值为何):

public String doSomething();
java

此消息处理程序方法是根据发送到与此处理程序连接的输入通道的消息来调用的。但是,没有映射任何 Message 数据,因此使 Message 作为事件或触发器来调用处理程序。输出根据前面描述的规则进行映射。

以下示例显示没有参数和 void 返回值:

public void soSomething();
java

此示例与上一个示例相同,但它不产生任何输出。

基于注解的映射

基于注解的映射是将消息映射到方法最安全且最不模糊的方法。以下示例展示了如何将方法显式映射到标题:

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

如您后面所见,如果没有注解,此签名将导致模棱两可的情况。但是,通过明确地将第一个参数映射到 Message 负载,将第二个参数映射到 someheader 消息头的值,我们避免了任何歧义。

以下示例几乎与前面的示例相同:

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

@RequestMapping 或任何其他非 Spring Integration 映射注解都是无关的,因此会被忽略,这使得第二个参数未映射。虽然第二个参数可以很容易地映射到有效负载,但只能有一个有效负载。因此,这些注解防止了此方法产生歧义。

以下示例展示了另一种类似的方法,如果没有注解来澄清意图的话,该方法将会产生歧义:

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

唯一的区别是第一个参数被隐式映射到消息有效负载。

以下示例显示了另一个如果没有注解肯定会被视为模棱两可的签名,因为它有超过两个参数:

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

这个例子会特别有问题,因为它的两个参数是 Map 实例。然而,使用基于注解的映射,歧义很容易避免。在这个例子中,第一个参数被映射到所有的消息头,而第二个和第三个参数分别映射到名为 'something' 和 'someotherthing' 的消息头的值。有效负载没有被映射到任何参数。

复杂场景

以下示例使用多个参数:

多个参数会在确定适当的映射时产生很多歧义。一般建议是使用 @Payload@Header@Headers 注解你的方法参数。本节中的示例展示了导致异常被抛出的歧义条件。

public String doSomething(String s, int i)
java

这两个参数的权重相等。因此,无法确定哪一个是要传输的数据。

下面的例子展示了类似的问题,只是有三个参数:

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

虽然 Map 可以很容易地映射到消息头,但没有办法确定如何处理这两个 String 参数。

以下示例显示了另一种模糊的方法:

public String foo(Map m, Map f)
java

虽然有人可能会争辩说一个 Map 可以映射到消息有效负载,另一个可以映射到消息头,但我们不能依赖顺序。

提示

任何具有多个方法参数的方法签名,如果参数不是 (Map, <T>) 且参数未标注,则会导致模棱两可的情况并触发异常。

下一组示例各自展示了多种导致歧义的方法。

具有多个方法的消息处理程序是根据前面描述的相同规则进行映射的(在示例中)。但是,某些场景可能看起来仍然令人困惑。

以下示例显示了多个具有合法(可映射且无歧义)签名的方法:

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

public String doSomething(Map m);
}
java

(无论方法名称相同还是不同都没有区别)。Message 可以映射到任一方法。当消息有效负载可以映射到 str 且消息头可以映射到 m 时,将调用第一种方法。第二种方法也可以通过仅将消息头映射到 m 而成为候选方法。更糟糕的是,这两个方法名称相同。乍一看,由于以下配置,这可能会显得模棱两可:

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

它之所以有效,是因为映射首先基于负载,然后才是其他所有内容。换句话说,第一个参数可以映射到负载的方法优先于所有其他方法。

现在考虑一个替代示例,它会产生真正模棱两可的条件:

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

public String doSomething(String str);
}
java

这两种方法都有可以映射到消息有效负载的签名。它们也有相同的名字。这样的处理器方法会触发异常。然而,如果方法名称不同,你可以使用 method 属性(在下一个示例中显示)影响映射。以下示例显示了具有两个不同方法名称的相同示例:

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

public String doSomethingElse(String str);
}
java

以下示例展示了如何使用 method 属性来指定映射:

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

因为配置明确映射了 doSomethingElse 方法,所以我们消除了歧义。