如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-doc.cadn.net.cn

消息映射规则与约定

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

场景示例

以下示例展示了一个未加注解的参数(对象或基本类型),它不是 MapProperties 类型的对象,且具有非 void 的返回类型:spring-doc.cadn.net.cn

public String doSomething(Object o);

输入参数是一个消息负载。 如果参数类型与消息负载不兼容,则会尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值将作为返回消息的负载被包含在内。spring-doc.cadn.net.cn

以下示例展示了一个未加注解的参数(对象或基本类型),它不是 Map 也不是 Properties,且返回类型为 Messagespring-doc.cadn.net.cn

public Message doSomething(Object o);

输入参数是一个消息负载。 如果参数类型与消息负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值是构造的新消息,该消息将被发送到下一个目的地。spring-doc.cadn.net.cn

以下示例展示了一个参数,该参数为消息(或其子类之一),并具有任意对象或基本类型的返回类型:spring-doc.cadn.net.cn

public int doSomething(Message msg);

输入参数本身是一个 Message。 返回值成为发送给下一个目的地的 Message 的有效负载。spring-doc.cadn.net.cn

以下示例展示了一个参数为 Message(或其子类之一),返回类型为 Message(或其子类之一)的情况:spring-doc.cadn.net.cn

public Message doSomething(Message msg);

输入参数本身是一个 Message。 返回值是一个新构造的 Message,该值将被发送到下一个目的地。spring-doc.cadn.net.cn

下面的示例展示了一个类型为 MapProperties 的单个参数,其返回类型为 Messagespring-doc.cadn.net.cn

public Message doSomething(Map m);

这一点有些有趣。 尽管乍一看,它似乎可以直接映射到消息头,但始终优先处理 Message 有效负载。 这意味着如果 Message 有效负载的类型为 Map,则该输入参数代表一个 Message 有效负载。 然而,如果 Message 有效负载不是 Map 类型,则转换服务不会尝试转换该有效负载,此时输入参数将映射到消息头。spring-doc.cadn.net.cn

以下示例显示了两个参数,其中一个是任意类型(对象或基本类型),且不是 MapProperties 类型的对象,另一个是 MapProperties 类型的对象(无论返回值如何):spring-doc.cadn.net.cn

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

此组合包含两个输入参数,其中一个是类型为 Map 的类型。 非 Map 的参数(无论顺序如何)被映射到 Message 负载,而 MapProperties(无论顺序如何)被映射到消息头,从而为您提供一种以 POJO 方式与 Message 结构交互的优雅方法。spring-doc.cadn.net.cn

以下示例显示无参数(无论返回值如何):spring-doc.cadn.net.cn

public String doSomething();

该消息处理器方法是根据发送到与该处理器连接的消息输入通道的消息来调用的。 然而,没有Message数据被映射,因此Message作为事件或触发器来调用该处理器。 输出根据前面描述的规则进行映射。spring-doc.cadn.net.cn

以下示例显示没有参数且返回 void:spring-doc.cadn.net.cn

public void soSomething();

此示例与上一个示例相同,但不会产生任何输出。spring-doc.cadn.net.cn

基于注解的映射

基于注解的映射是将消息映射到方法的最安全且歧义最少的方法。 以下示例展示了如何显式地将方法映射到头部:spring-doc.cadn.net.cn

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

如您稍后将看到的,如果没有注解,此签名将导致歧义条件。 然而,通过将第一个参数显式映射到 Message 负载,并将第二个参数映射到 someheader 消息头的值,我们可以避免任何歧义。spring-doc.cadn.net.cn

以下示例与前面的示例几乎相同:spring-doc.cadn.net.cn

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

@RequestMapping 或任何其他非 Spring Integration 映射注解均无关紧要,因此会被忽略,导致第二个参数未被映射。 尽管第二个参数很容易映射到负载(payload),但只能有一个负载。 因此,这些注解避免了该方法的歧义性。spring-doc.cadn.net.cn

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

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

唯一的区别是第一个参数隐式映射到消息负载。spring-doc.cadn.net.cn

以下示例展示了另一个签名,如果没有注解,它肯定会被视为歧义的,因为它有两个以上的参数:spring-doc.cadn.net.cn

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

此示例会特别成问题,因为其两个参数都是 Map 实例。 然而,基于注解的映射可以轻松避免这种歧义。 在此示例中,第一个参数被映射到所有消息头,而第二个和第三个参数则分别映射到名为 'something' 和 'someotherthing' 的消息头的值。 负载(payload)未被映射到任何参数。spring-doc.cadn.net.cn

复杂场景

以下示例使用了多个参数:spring-doc.cadn.net.cn

多个参数在确定适当的映射时可能会产生大量歧义。 一般建议是使用方法参数注解 @Payload@Header@Headers。 本节中的示例展示了会导致抛出异常的歧义条件。spring-doc.cadn.net.cn

public String doSomething(String s, int i)

这两个参数权重相等。 因此,无法确定哪一个是有效载荷。spring-doc.cadn.net.cn

下面的示例展示了一个类似的问题,只是有三个参数:spring-doc.cadn.net.cn

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

虽然 Map 可以轻松映射到消息头,但无法确定如何处理这两个 String 参数。spring-doc.cadn.net.cn

以下示例展示了另一个歧义方法:spring-doc.cadn.net.cn

public String foo(Map m, Map f)

尽管有人可能会争辩说一个 Map 可以映射到消息负载,另一个映射到消息头,但我们不能依赖顺序。spring-doc.cadn.net.cn

任何具有多个方法参数且参数未加注解的方法签名,如果参数数量不是 (0, 1),将导致条件歧义并触发异常。

下一组示例分别展示了会导致歧义的多种方法。spring-doc.cadn.net.cn

具有多个方法的消息处理器基于前面描述(在示例中)的相同规则进行映射。 然而,某些场景可能仍然令人困惑。spring-doc.cadn.net.cn

以下示例展示了多个具有合法(可映射且无歧义)签名的方法:spring-doc.cadn.net.cn

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

    public String doSomething(Map m);
}

(方法名称是否相同并无影响)。 Message可以映射到任意一个方法。 当消息负载可映射为str且消息头可映射为m时,将调用第一个方法。 第二个方法也可能成为候选,只需将消息头映射为m即可。 更糟糕的是,这两个方法具有相同的名称。 起初,由于以下配置,这看起来可能具有歧义性:spring-doc.cadn.net.cn

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

这是因为映射首先基于负载,其次才是其他内容。 换句话说,其第一个参数可以映射到负载的方法优先于所有其他方法。spring-doc.cadn.net.cn

现在考虑一个替代示例,它会产生一个真正的歧义条件:spring-doc.cadn.net.cn

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

    public String doSomething(String str);
}

这两种方法的签名都可以映射到消息负载。 它们还具有相同的名称。 此类处理程序方法将触发异常。 但是,如果方法名称不同,则可以通过 method 属性影响映射(如下一个示例所示)。 以下示例展示了具有两个不同方法名称的相同示例:spring-doc.cadn.net.cn

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

    public String doSomethingElse(String str);
}

下面的示例展示了如何使用 method 属性来规定映射关系:spring-doc.cadn.net.cn

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

由于配置显式映射了 doSomethingElse 方法,我们已消除了歧义。spring-doc.cadn.net.cn