过滤器

消息过滤器用于根据某些条件(例如消息头值或消息内容本身)决定是否应传递或丢弃 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 抽象可以作为 Lambda 在 filter() 定义中使用

  • 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-channeloutput-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作为属性或子元素的示例也可以应用于转换器、路由器、拆分器、服务激活器和标头丰富器元素中。给定组件类型的语义和作用将影响对评估结果的解释,就像方法调用的返回值将被解释一样。例如,表达式可以返回字符串,这些字符串将被路由器组件视为消息通道名称。但是,在 Spring Integration 中的所有核心 EIP 组件中,将表达式针对消息(作为根对象)进行评估以及解析以“@”为前缀的 bean 名称的底层功能是一致的。

使用注解配置过滤器

以下示例展示了如何使用注解配置过滤器

public class PetFilter {
    ...
    @Filter  (1)
    public boolean dogsOnly(String input) {
        ...
    }
}
1 一个注解,指示此方法将用作过滤器。如果此类将用作过滤器,则必须指定它。

@Filter注解提供了 XML 元素提供的所有配置选项。

过滤器可以从 XML 中显式引用,或者如果在类上定义了@MessageEndpoint注解,则可以通过类路径扫描自动检测到它。