消息映射规则和约定
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` 可以映射到消息载荷,另一个映射到消息头,但我们不能依赖顺序。
任何包含多于一个未加注解的方法参数,并且不符合默认规则可唯一确定映射条件的签名,都会导致模糊条件并触发异常。 |
下一组示例分别展示了导致模糊的多个方法。
具有多个方法的消息处理器根据前面(在示例中)描述的相同规则进行映射。然而,某些场景可能仍然看起来令人困惑。
以下示例展示了具有合法(可映射且无歧义)签名的多个方法
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` 方法,我们消除了歧义。