过滤器
消息过滤器用于根据某些标准(例如消息头值或消息内容本身)来决定是否应传递或丢弃一条Message。因此,消息过滤器类似于路由器,不同之处在于,对于从过滤器输入通道接收到的每条消息,该消息可能被发送到过滤器的输出通道,也可能不被发送。与路由器不同,它不决定将消息发送到哪个消息通道,而仅决定是否发送该消息。
正如我们在本节后面所描述的,过滤器还支持一个丢弃通道。在某些情况下,它可以基于布尔条件扮演一个非常简单的路由器(或“交换机”)的角色。
在Spring Integration中,您可以将消息过滤器配置为消息端点,该端点委托给 MessageSelector 接口的实现。该接口本身非常简单,如下面的清单所示:
public interface MessageSelector {
boolean accept(Message<?> message);
}
MessageFilter 构造函数接受一个选择器实例,如下例所示:
MessageFilter filter = new MessageFilter(someSelector);
使用 Java、Groovy 和 Kotlin DSL 配置过滤器
Java DSL 提供的 IntegrationFlowBuilder(同时也是 Groovy 和 Kotlin DSL 的基础)为 filter() 操作符提供了多个重载方法。上文提到的 MessageSelector 抽象可以在 filter() 定义中作为 Lambda 表达式使用:
- Java DSL
- Kotlin DSL
- Groovy DSL
@Bean
public IntegrationFlow someFlow() {
return f -> f
.<String>filter((payload) -> !"junk".equals(payload));
}
@Bean
fun someFlow() =
integrationFlow {
filter<String> { it != "junk" }
}
@Bean
someFlow() {
integrationFlow {
filter String, { it != 'junk' }
}
}
有关 DSL 的更多信息,请参阅相应章节:
使用 XML 配置过滤器
结合命名空间和SpEL,你可以用极少的Java代码配置强大的过滤器。
您可以使用 <filter> 元素来创建消息选择端点。除了 input-channel 和 output-channel 属性外,它还需要一个 ref 属性。ref 可以指向一个 MessageSelector 实现,如下例所示:
<int:filter input-channel="input" ref="selector" output-channel="output"/>
<bean id="selector" class="example.MessageSelectorImpl"/>
或者,你也可以添加 method 属性。在这种情况下,ref 属性可以引用任何对象。被引用的方法可以接收 Message 类型或入站消息的有效负载类型。该方法必须返回一个布尔值。如果方法返回 'true',消息将被发送到输出通道。以下示例展示了如何配置使用 method 属性的过滤器:
<int:filter input-channel="input" output-channel="output"
ref="exampleObject" method="someBooleanReturningMethod"/>
<bean id="exampleObject" class="example.SomeObject"/>
如果选择器或适配的POJO方法返回false,可以通过几个设置来控制被拒绝消息的处理方式。默认情况下,如果按前述示例配置,被拒绝的消息会被静默丢弃。若希望拒绝消息时触发错误状态,可将throw-exception-on-rejection属性设置为true,如下例所示:
<int:filter input-channel="input" ref="selector"
output-channel="output" throw-exception-on-rejection="true"/>
若要将被拒绝的消息路由到特定通道,请将该引用作为 discard-channel 提供,如下例所示:
<int:filter input-channel="input" ref="selector"
output-channel="output" discard-channel="rejectedMessages"/>
如果 throwExceptionOnRejection == false 且未提供 discardChannel,消息将被静默丢弃,并且 o.s.i.filter.MessageFilter 实例仅会发出关于此丢弃消息的警告日志(从版本 6.1 开始)。若要在日志中无警告地丢弃消息,可以在过滤器上将 NullChannel 配置为 discardChannel。框架的目标是默认不完全静默,如果这是期望的行为,则需要设置一个明确的选项。
另请参阅通知过滤器。
消息过滤器通常与发布-订阅通道结合使用。多个过滤器端点可以订阅同一个通道,它们决定是否将消息传递给下一个端点(可以是任何支持的类型,例如服务激活器)。这提供了一种响应式替代方案,相较于使用具有单个点对点输入通道和多个输出通道的消息路由器这种更主动的方法。
我们建议,如果自定义过滤器实现在其他 <filter> 定义中被引用,则使用 ref 属性。然而,如果自定义过滤器实现的作用域仅限于单个 <filter> 元素,则应提供内部 bean 定义,如下例所示:
<int:filter method="someMethod" input-channel="inChannel" output-channel="outChannel">
<beans:bean class="org.foo.MyCustomFilter"/>
</filter>
不允许在同一个 <filter> 配置中同时使用 ref 属性和内部处理器定义,因为这会造成歧义条件并引发异常。
如果 ref 属性引用了一个继承自 MessageFilter 的 bean(例如框架自身提供的过滤器),配置将通过直接将输出通道注入到过滤器 bean 中进行优化。在这种情况下,每个 ref 必须指向一个独立的 bean 实例(或一个 prototype 作用域的 bean),或者使用内部的 <bean/> 配置类型。然而,此优化仅适用于您在过滤器的 XML 定义中未提供任何过滤器特定属性的情况。如果您无意中从多个 bean 引用了同一个消息处理器,将会得到一个配置异常。
随着 SpEL 支持的引入,Spring Integration 为过滤器元素添加了 expression 属性。对于简单的过滤器,它可以完全避免使用 Java,如下例所示:
<int:filter input-channel="input" expression="payload.equals('nonsense')"/>
作为表达式属性值传递的字符串会被评估为一个SpEL表达式,评估上下文中包含消息。如果必须在应用程序上下文的范围内包含表达式的结果,可以使用 #{} 表示法,如 SpEL 参考文档 中所定义,如下例所示:
<int:filter input-channel="input"
expression="payload.matches(#{filterPatterns.nonsensePattern})"/>
如果表达式本身需要是动态的,你可以使用一个 'expression' 子元素。这提供了一种间接解析表达式的方式,通过其键从 ExpressionSource 中解析。这是一个策略接口,你可以直接实现它,或者可以依赖 Spring Integration 中提供的一个版本,该版本从“资源包”加载表达式,并能在给定秒数后检查修改。所有这些都在以下配置示例中进行了演示,如果底层文件已被修改,表达式可以在一分钟内重新加载:
<int:filter input-channel="input" output-channel="output">
<int:expression key="filterPatterns.example" source="myExpressions"/>
</int:filter>
<beans:bean id="myExpressions"
class="o.s.i.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="config/integration/expressions"/>
<beans:property name="cacheSeconds" value="60"/>
</beans:bean>
如果 ExpressionSource bean 被命名为 expressionSource,则无需在 <expression> 元素上提供 source 属性。但在前面的示例中,为了保持完整性我们仍然展示了该属性。
'config/integration/expressions.properties' 文件(或任何带有区域设置扩展名的更具体版本,将按照资源包加载的典型方式解析)可以包含键/值对,如下例所示:
filterPatterns.example=payload > 100
所有使用 expression 作为属性或子元素的示例同样适用于 transformer、router、splitter、service-activator 和 header-enricher 元素。给定组件类型的语义和角色会影响对评估结果的解释,其方式与方法调用返回值的解释类似。例如,表达式可以返回字符串,这些字符串将被 router 组件视为消息通道名称。然而,以消息作为根对象评估表达式,并在以 '@' 为前缀时解析 bean 名称的基础功能,在 Spring Integration 的所有核心 EIP 组件中是一致的。
通过注解配置过滤器
以下示例展示了如何使用注解配置过滤器:
public class PetFilter {
...
@Filter 1
public boolean dogsOnly(String input) {
...
}
}
表示此方法将用作过滤器的注解。如果此类要作为过滤器使用,则必须指定此注解。
@Filter 注解同样支持 XML 元素提供的所有配置选项。
过滤器既可以从XML中显式引用,也可以在类上定义@MessageEndpoint注解时,通过类路径扫描自动检测。
另请参阅使用注解通知端点。