消息映射规则与约定
Spring Integration 通过依赖一些默认规则和定义某些约定,实现了一种灵活的机制来将消息映射到方法及其参数,而无需提供额外的配置。以下各节中的示例阐明了这些规则。
示例场景
以下示例展示了一个没有注解的单个参数(对象或原始类型),它不是 Map
或 Properties
对象,并且具有非 void
返回类型:
public String doSomething(Object o);
输入参数是消息负载。如果参数类型与消息负载不兼容,则会尝试使用 Spring 3.0 提供的转换服务进行转换。返回值作为返回消息的负载。
以下示例展示了一个没有注解的单个参数(对象或原始类型),它不是 Map
或 Properties
,并且具有 Message
返回类型:
public Message doSomething(Object o);
输入参数是消息负载。如果参数类型与消息负载不兼容,则会尝试使用 Spring 3.0 提供的转换服务进行转换。返回值是一个新构建的消息,它被发送到下一个目的地。
以下示例展示了一个 Message
(或其子类之一)类型的单个参数,具有任意对象或原始返回类型:
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
方法,我们消除了模糊性。