消息映射规则和约定

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

示例场景

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

public String doSomething(Object o);

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

以下示例展示了一个没有注解的单个参数(对象或基本类型),它不是 MapProperties,并且具有 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,并发送到下一个目的地。

以下示例展示了一个类型为 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 充当调用处理程序的事件或触发器。输出根据前面描述的规则进行映射。

以下示例展示了没有参数且返回类型为 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 方法,我们消除了歧义。

© . This site is unofficial and not affiliated with VMware.