架构

谓词

师成师成· 更新于 2026-09-28· 阅读 13 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

谓词

表达式与谓词可用于在 DSL 中创建各种企业集成模式,例如基于内容的路由器(Content Based Router)EIP 或收件人列表(Recipient List)EIP。

为了支持动态规则,Camel 使用多种不同的语言,提供了可插拔的 Predicate 策略。

Simple 语言常被用作 Camel EIP 的谓词和表达式。

谓词 API

Camel 谓词的 API 定义在 org.apache.camel.Predicate 接口中,如下所示:

public interface Predicate {

    /**
     * Evaluates the predicate on the message exchange and returns true if this
     * exchange matches the predicate
     *
     * @param exchange the message exchange
     * @return true if the predicate matches
     */
    boolean matches(Exchange exchange);

}

Predicate 会被求值为一个布尔值,因此结果只能是 true 或 false。这正是谓词如此强大的原因——它常被用来控制消息的路由,决定消息应当走哪条路径。

一个简单的例子是使用基于内容的路由器 EIP,根据头信息的值来路由 Exchange:

  • Java
  • XML
  • YAML
from("jms:queue:order")
   .choice()
      .when(header("type").isEqualTo("widget")).to("bean:widgetOrder")
      .when(header("type").isEqualTo("wombat")).to("bean:wombatOrder")
   .otherwise()
      .to("bean:miscOrder")
   .end();
<route>
    <from uri="jms:queue:order"/>
    <choice>
        <when>
            <simple>${header.type} == 'widget'</simple>
            <to uri="bean:widgetOrder"/>
        </when>
        <when>
            <simple>${header.type} == 'wombat'</simple>
            <to uri="bean:wombatOrder"/>
        </when>
        <otherwise>
            <to uri="bean:miscOrder"/>
        </otherwise>
    </choice>
</route>
- route:
    from:
      uri: jms:queue:order
      steps:
        - choice:
            when:
              - simple: "${header.type} == 'widget'"
                steps:
                  - to:
                      uri: bean:widgetOrder
              - simple: "${header.type} == 'wombat'"
                steps:
                  - to:
                      uri: bean:wombatOrder
            otherwise:
              steps:
                - to:
                    uri: bean:miscOrder

在上面的路由中,谓词(Predicate) 是 header("type").isEqualTo("widget"),因为它被构建为一个表达式(Expression),并作为谓词(Predicate) 来求值。为此,各种构建器类帮助我们创建简洁流畅的语法。isEqualTo 是一个构建器方法,它根据输入返回一个谓词(Predicate)。

有时候流式构建器会变得很长,阅读起来有点复杂,这时你可以在路由之外定义你的谓词,然后在路由中直接引用该谓词:

Predicate isWidget = header("type").isEqualTo("widget");

然后你可以在路由中这样引用它:

纯 Java:在路由中使用谓词变量

from("jms:queue:order")
   .choice()
      .when(isWidget).to("bean:widgetOrder")
      .when(isWombat).to("bean:wombatOrder")
   .otherwise()
      .to("bean:miscOrder")
   .end();

对谓词取反

你可以使用 PredicateBuilder 上的 not 方法对谓词取反。

首先,我们导入静态方法 not,这样可以让路由代码简洁易读:

import static org.apache.camel.builder.PredicateBuilder.not;

然后,我们就可以像示例中那样,用它包裹现有的谓词并对其取反:

  • Java
  • XML
  • YAML
from("direct:start")
    .choice()
        .when(not(header("username").regex("goofy|pluto"))).to("mock:people")
        .otherwise().to("mock:animals")
    .end();
<route>
    <from uri="direct:start"/>
    <choice>
        <when>
            <simple>${header.username} not regex 'goofy|pluto'</simple>
            <to uri="mock:people"/>
        </when>
        <otherwise>
            <to uri="mock:animals"/>
        </otherwise>
    </choice>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - choice:
            when:
              - simple: "${header.username} not regex 'goofy|pluto'"
                steps:
                  - to:
                      uri: mock:people
            otherwise:
              steps:
                - to:
                    uri: mock:animals

复合谓词

你还可以使用 and、or、not 等布尔运算符以及许多其他运算符来创建复合谓词。

目前,此功能仅在基于 Java 的 DSL 中可用,在其他 DSL(如 XML)中不可用。

借助 PredicateBuilder 类,你可以基于逻辑运算符和比较运算符,将来自不同表达式语言的谓词组合起来:

  • not、and、or
  • isNull、isNotNull
  • isEqualTo、isGreaterThan、isLessThan
  • startsWith、endsWith
  • in("X 个谓词中任意一个为真")

此外,使用 PredicateBuilder 还可以创建正则表达式并将其用作谓词,将其应用于表达式的结果,例如

PredicateBuilder.regex(header("foo"), "\d\{4}");

正则表达式应用于 foo 头。

也可以组合使用不同的表达式语言,例如:

PredicateBuilder.and(XPathBuilder.xpath("/bookings/flights"), simple("${exchangeProperty.country = 'Spain'}"))

下面的示例演示了更多用例:

仅 Java:使用 PredicateBuilder 构建复合谓词

// We define 3 predicates based on some user roles
// we have static imported and/or from org.apache.camel.builder.PredicateBuilder

// First we have a regular user that is just identified having a username header
Predicate user = header("username").isNotNull();

// The admin user must be a user AND have a admin header as true
Predicate admin = and(user, header("admin").isEqualTo("true"));

// And God must be an admin and (either have type god or a special message containing Camel Rider)
Predicate god = and(admin, or(body().contains("Camel Rider"), header("type").isEqualTo("god")));

// As you can see with the predicates above we can stack them to build compound predicates

// In our route below we can create a nice content based router based on the predicates we
// have defined. Then the route is easy to read and understand.
// We encourage you to define complex predicates outside the fluent router builder as
// it will just get a bit complex for humans to read
from("direct:start").choice()
    .when(god).to("mock:god")
    .when(admin).to("mock:admin")
    .when(user).to("mock:user")
    .otherwise().to("mock:guest")
.end();

评论

登录后参与评论

正在加载评论…