Message Mapping Rules and Conventions

Spring Integration 实现了一个灵活的工具,无需提供额外配置,即可将消息映射到方法及其参数,方法是使用一些默认规则和定义某些约定。以下部分的示例阐述了这些规则。

Sample Scenarios

以下示例展示了一个未注释的单个参数(对象或基本类型),它不是一个 Map 或带有非空返回类型的 Properties 对象:

public String doSomething(Object o);

输入参数是一个消息负载。如果参数类型与消息负载不兼容,则尝试使用 Spring 3.0 提供的转换服务转换它。返回值作为返回消息的负载合并。

以下示例展示了一个未注释的单个参数(对象或基本类型),它不是一个 Map 或带有 Message 返回类型的 Properties

public Message doSomething(Object o);

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

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

public int doSomething(Message msg);

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

以下示例展示了一个单个参数,它是一个 Message(或其子类),并带有 Message(或其子类)作为返回类型:

public Message doSomething(Message msg);

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

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

public Message doSomething(Map m);

这个有点意思。虽然一开始看起来它必定是直接映射到消息头,但始终优先考虑 Message 负载。这意味着,如果 Message 负载的类型是 Map,则此输入参数表示一个 Message 负载。但是,如果 Message 负载不是 Map 类型,则转换服务不会尝试转换负载,并且输入参数将映射到消息头。

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

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

此组合包含两个输入参数,其中一个类型为 Map。非 Map 参数(无论顺序如何)映射到 Message 负载,MapProperties(无论顺序如何)映射到消息头,为您提供了一种与 Message 结构交互得很棒的 POJO 方式。

以下示例展示了没有参数(无论返回):

public String doSomething();

此消息处理程序方法根据发送到此处理程序连接的输入通道的消息进行调用。但是,没有映射的 Message 数据,因此使得 Message 充当事件或触发器来调用处理程序。输出根据 described earlier 规则进行映射。

以下示例展示了没有参数和空返回:

public void soSomething();

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

Annotation-based Mapping

基于注释的映射是将消息映射到方法最安全、最不模棱两可的方法。以下示例说明如何明确地将方法映射到标头:

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 实例。但是,使用基于注释的映射,可以轻松避免歧义。在此示例中,第一个参数被映射到所有消息头,而第二个和第三个参数被映射到名为 somethingsomeotherthing 的消息头的值。有效负载未映射到任何参数。

Complex Scenarios

以下示例使用多个参数:

多个参数可能会在确定适当的映射方面造成很大的歧义。一般的建议是用 @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 方法,因此我们消除了歧义。