Filter

消息过滤器用于根据某些条件(例如消息头值或消息内容本身)决定是否传递或丢弃 Message。因此,消息过滤器与路由器类似,不同之处在于,对于从过滤器的输入通道接收的每条消息,这同一条消息可能被发送或不被发送到过滤器的输出通道。与路由器不同的是,它不会决定将消息发送到哪个消息通道,而只决定是否发送消息。

如本节后面所述,过滤器还支持放弃通道。在某些情况下,它可以根据布尔条件扮演非常简单的路由器(或"`switch`")的角色。

在 Spring Integration 中,您可以将消息过滤器配置为委托给 MessageSelector 接口实现的消息端点。如下表所示,该接口本身非常简单:

public interface MessageSelector {

    boolean accept(Message<?> message);

}

MessageFilter 构造函数接受一个选择器实例,如下例所示:

MessageFilter filter = new MessageFilter(someSelector);

Configuring a Filter with Java, Groovy and Kotlin DSLs

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 的更多信息:

Configuring a Filter with 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。框架的目标默认不是完全静默,需要设置一个显式选项(如果这是所需操作)。

另请参阅 Advising Filters

消息过滤器通常与发布-订阅通道结合使用。许多过滤器端点可能订阅同一通道,它们决定是否将消息传递给下一个端点,该端点可以是任何受支持类型(例如服务激活器)。这为使用具有单点对点输入通道和多个输出通道的消息路由器的更主动的方法提供了一种反应式替代方法。

如果自定义过滤器实现被引用在其他 <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 向 filter 元素添加了 expression 属性。它可用于完全避免使用 Java 来实现简单过滤器,如下面的示例所示:

<int:filter input-channel="input" expression="payload.equals('nonsense')"/>

传递为表达式属性值字符串在评估上下文中作为 SpEL 表达式求值。如果您必须将表达式的结果包含在应用程序上下文的范围内,那么可以使用 `#{}`符号,如 SpEL reference documentation中定义的,如下例所示:

<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 用作属性或子元素的示例也适用于变换器、路由器、分隔器、服务激活器和标题丰富元素中。给定组件类型的语义和作用会影响对评估结果的解释,就像解释方法调用的返回值一样。例如,表达式可以返回字符串,而这些字符串由路由器组件视作消息信道名称。然而,针对消息评估表达式(以消息作为根对象)和解析以“@”为前缀的 Bean 名称的基本功能,在 Spring 集成内的所有核心 EIP 组件中是一致的。

Configuring a Filter with Annotations

下面的示例展示了如何通过使用注解来配置一个过滤器:

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

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

过滤器可以从 XML 中显式引用,也可以在类上定义 @MessageEndpoint 注解时通过类路径扫描自动检测。