操作指南

如何使用 Camel 属性占位符

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

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

属性占位符

Camel 对属性占位符提供了广泛的支持,可以在你的 Camel 路由、端点、DSL、路由配置、Bean 集成 以及其他地方几乎任意位置使用。

属性占位符用于定义一个占位符来代替实际值。这一点非常重要,因为你需要能够让应用程序支持外部配置,例如网络地址、端口号、认证凭据、登录令牌以及通用的配置等值。

Properties 组件

Camel 在核心中开箱即用地提供了 Properties,它负责处理和解析属性占位符。

有关如何配置 Camel 以确定从哪些位置加载属性,请参阅 Properties 文档。

属性占位符语法

要获取某个 Camel 属性的值,可在属性占位符中指定其键名,语法如下:{{key}}

例如:

{{file.uri}}

其中 file.uri 即为属性键。

属性占位符可以用来指定端点 URI 的部分或全部内容,方法是在 URI 的字符串定义中嵌入一个或多个占位符。

例如,如下所示的路由:

  • Java
  • XML
  • YAML
from("{{file.uri}}")
  .to("kafka:{{orderTopic}}");
    <route>
        <from uri="{{file.uri}}"/>
        <to uri="kafka:{{orderTopic}}"/>
    </route>
- route:
    from:
      uri: "{{file.uri}}"
      steps:
        - to:
            uri: "kafka:{{orderTopic}}"

然后可以在 application.properties 中声明该占位符:

file.uri = file:/var/orders/inbox?recursive=true

使用带默认值的属性占位符

如果具有该键的属性不存在,你可以指定一个默认值,其中冒号之后的文本即为默认值:

{{file.url:/some/path}}

在这种情况下,默认值为 /some/path。

使用可选的属性占位符

Camel 精巧的属性占位符功能支持可选占位符,这类占位符通过在键名前加 ?(问号)前缀来声明,如下所示:

{{?myBufferSize}}

如果该键对应的值存在,则使用该值;而如果该键不存在,Camel 也知道该如何处理。例如,当在端点中使用时:

file:foo?bufferSize={{?myBufferSize}}

那么,只有当占位符存在时,bufferSize 选项才会在端点上进行配置。否则,该选项不会设置到端点上,这意味着端点会被重构为:

file:foo

那么 bufferSize 选项未被声明,这将允许 Camel 使用 bufferSize 的标准默认值(如果存在的话)。

反转布尔值

如果属性占位符是一个布尔值,那么可以通过在键前加上 ! 前缀来取反(反转)该值。

例如,假设我们有这样一个属性:

integration.ftpEnabled=true

然后,可以用它来控制哪些路由应该自动启动。

  • Java
  • XML
  • YAML
from("ftp:....").autoStartup("{{integration.ftpEnabled}}")
    .to("kafka:cheese");

from("jms:....").autoStartup("{{!integration.ftpEnabled}}")
    .to("kafka:cheese");
<route autoStartup="{{integration.ftpEnabled}}">
    <from uri="ftp:...."/>
    <to uri="kafka:cheese"/>
</route>

<route autoStartup="{{!integration.ftpEnabled}}">
    <from uri="jms:...."/>
    <to uri="kafka:cheese"/>
</route>
- route:
    autoStartup: "{{integration.ftpEnabled}}"
    from:
      uri: ftp:....
      steps:
        - to:
            uri: kafka:cheese
- route:
    autoStartup: "{{!integration.ftpEnabled}}"
    from:
      uri: jms:....
      steps:
        - to:
            uri: kafka:cheese

在上面的示例中,FTP 路由或 JMS 路由应当只启动其中一个。也就是说,如果 FTP 已启用,则 JMS 应被禁用,反之亦然。我们可以通过否定 JMS 路由中的 autoStartup 来实现这一点,即使用 !integration.ftpEnabled 作为键。

使用属性占位符

在端点 URI 中使用属性占位符时,应使用 {{key}} 这种语法,如下面的示例所示:

cool.end = mock:result
where = cheese
  • Java
  • XML
  • YAML
from("direct:start")
    .to("{{cool.end}}");
<route>
  <from uri="direct:start"/>
  <to uri="{{cool.end}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "{{cool.end}}"

属性占位符也可以只作为端点 URI 中的一个组成部分。

一个常见的用例,是在端点选项中使用占位符,例如文件端点的写缓冲区大小:

buf = 8192
  • Java
  • XML
  • YAML
from("direct:start")
    .to("file:outbox?bufferSize={{buf}}");
<route>
  <from uri="direct:start"/>
  <to uri="file:outbox?bufferSize={{buf}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: file:outbox
            parameters:
              bufferSize: "{{buf}}"

然而,占位符可以出现在任何位置,因此它也可以是 mock 端点的名称

  • Java
  • XML
  • YAML
from("direct:start")
    .to("mock:{{where}}");
<route>
    <from uri="direct:start"/>
    <to uri="mock:{{where}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "mock:{{where}}"

在上面的示例中,mock 端点已硬编码为以 mock: 开头,而 where 占位符的值为 cheese,因此解析后的 URI 变为 mock:cheese。

引用其他属性的属性占位符(嵌套占位符)

属性之间也可以相互引用,例如:

cool.foo=result
cool.concat=mock:{{cool.foo}}

注意 cool.concat 如何引用了另一个属性(见上文)。

然后你可以在 Camel 路由中使用 cool.concat 占位符:

  • Java
  • XML
  • YAML
from("direct:start")
    .to("{{cool.concat}}");
<route>
    <from uri="direct:start"/>
    <to uri="{{cool.concat}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "{{cool.concat}}"

关闭嵌套占位符(高级)

如果占位符的值中包含会干扰属性占位符语法 {{ 和 }} 的数据(例如 JSon 数据),则可以在键名中通过 ?nested=false 显式关闭嵌套占位符,示例如下:

  • Java
  • XML
  • YAML
from("direct:start")
    .to("elasticsearch:foo?query={{myQuery?nested=false}}");
<route>
    <from uri="direct:start"/>
    <to uri="elasticsearch:foo?query={{myQuery?nested=false}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: elasticsearch:foo
            parameters:
              query: "{{myQuery?nested=false}}"

在上面的示例中,占位符 myQuery 的占位符值如下

myQuery = {"query":{"match_all":{}}}

注意,JSON 查询以 }} 结尾,这会干扰 Camel 属性占位符的语法。

嵌套占位符也可以在 Properties 组件上全局关闭,例如:

CamelContext context = ...
context.getPropertiesComponent().setNestedPlaceholder(false);

在 JSON 查询以 }} 结尾并与 Camel 属性占位符语法冲突的情况下,也可以使用转义,详见下一节。

转义属性占位符

如果双花括号被第三方库使用,属性占位符可能会带来问题,例如 ElasticSearch 中类型为 {"query":{"match_all":{}}} 的查询。

为了解决这个问题,可以用反斜杠对双花括号进行转义,例如 \{{ property-name \}}。这样它就不会被解释为需要解析的属性占位符,而是会被解析为 {{ property-name }}。

上面的例子可以使用转义(注意该值以 \\}} 结尾)。

myQuery = {"query":{"match_all":{}\\}}

如果由于某种原因,双花括号前的反斜杠不能被解释为转义字符,可以在它前面再加一个反斜杠将其转义,这样它就会被看作一个普通的反斜杠。

多次使用属性占位符

当然,你也可以多次使用占位符:

cool.start=direct:start
cool.showid=true
cool.result=result

并且在这条路由中我们使用了 cool.start 两次:

  • Java
  • XML
  • YAML
from("{{cool.start}}")
    .to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
    .to("mock:{{cool.result}}");
<route>
    <from uri="{{cool.start}}"/>
    <to uri="log:{{cool.start}}?showBodyType=false&amp;showExchangeId={{cool.showid}}"/>
    <to uri="mock:{{cool.result}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "log:{{cool.start}}"
            parameters:
              showBodyType: "false"
              showExchangeId: "{{cool.showid}}"
        - to:
            uri: "mock:{{cool.result}}"

在消费者和生产者模板中使用属性占位符

在使用 ProducerTemplate 时同样可以使用属性占位符,例如:

template.sendBody("{{cool.start}}", "Hello World");

使用 ConsumerTemplate 时也可以这样做,例如:

Object body = template.receiveBody("{{cool.start}}");

从 Java 代码中解析属性占位符

如果你需要在某些 Java 代码中解析属性占位符,Camel 提供了两个 API:

  • 你可以使用 PropertiesComponent 上的 resolveProperty 方法,从 Java 代码中解析单个属性。
  • 使用 CamelContext 上的 resolvePropertyPlaceholders 方法,解析字符串中的一个或多个属性占位符。

例如,要解析键为 foo 的占位符,可以这样做:

Optional<String> prop = camelContext.getPropertiesComponent().resolveProperty("foo");
if (prop.isPresent()) {
    String value = prop.get();
    ....
}

这个 API 用于查询单个属性,并返回 java.util.Optional 类型。

CamelContext 还提供了另一个 API,能够同时解析多个占位符,并对输入字符串进行占位符插值。让我们通过一个例子来解释这一点:

String msg = camelContext.resolvePropertyPlaceholders("{{greeting}} Camel user, Camel is {{cool}} dont you think?");

输入字符串是一段包含两个占位符的文本语句,这些占位符将被解析,例如:

greeting = Hi
cool = awesome

将解析为:

Hi Camel user, Camel is awesome dont you think?

在云上解析属性占位符

当你的 Camel 应用运行在云端时,你可能希望自动扫描任意 ConfigMap 或 Secret,就像它们是应用属性文件一样。给定如下 Secret:

apiVersion: v1
data:
  my-property: Q2FtZWwgNC44
kind: Secret
metadata:
  name: my-secret
type: Opaque

你可以将其挂载到 Pod 容器中,例如挂载到 /etc/camel/conf.d/_secrets/my-secret。然后,只需通过应用属性 camel.main.cloud-properties-location = /etc/camel/conf.d/_secrets/my-secret 告知 Camel 应用程序到哪里扫描配置即可。该属性是一个以逗号分隔的值,因此你可以按需添加任意数量的 Secret/ConfigMap。

在运行时,你就能像使用其他属性一样,以 {{ my-property }} 的形式透明地读取该配置。

同样的配置同样适用于 ConfigMap。

遗留 Spring XML 文件的特殊功能

Apache Camel 诞生于十多年前,那时 Spring 框架被大量使用,且以 Spring XML 文件的形式进行配置。这意味着 Camel 拥有一些仅适用于 Spring XML 的特殊功能。

在 Spring XML 文件中对任意类型的属性使用属性占位符

此前,在 XML DSL 中只有 xs:string 类型的属性支持占位符。例如,超时属性通常是 xs:int 类型,因此你无法将字符串值设置为占位符键。现在,通过一个特殊的占位符命名空间,这一限制已被解除。

在下面的示例中,我们为命名空间 http://camel.apache.org/schema/placeholder 使用 prop 前缀。这样我们就可以使用 prop: 作为前缀,在 Spring XML 文件中为任意类型的 XML 属性配置占位符。

在下面的示例中,我们希望为 Multicast EIP 的 stopOnException 选项使用占位符。stopOnException 是 xs:boolean 类型,因此我们无法像下面这样进行配置:

<multicast stopOnException="{{stop}}">
   ...
</multicast>

相反,我们必须使用 prop: 命名空间,因此需要在 XML 文件顶部的 <beans> 标签中声明该命名空间。

配置选项时,我们需要使用 prop:optionName 的形式,如下所示:

<multicast prop:stopOnException="stop">
  ...
</multicast>

完整示例如下:

Spring XML

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:prop="http://camel.apache.org/schema/placeholder"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <bean id="damn" class="java.lang.IllegalArgumentException">
        <constructor-arg index="0" value="Damn"/>
    </bean>

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <propertyPlaceholder id="properties" location="classpath:myprop.properties"/>
        <route>
            <from uri="direct:start"/>
            <!-- use prop namespace, to define a property placeholder, which maps to option stopOnException={{stop}} -->
            <multicast prop:stopOnException="stop">
                <to uri="mock:a"/>
                <throwException ref="damn"/>
                <to uri="mock:b"/>
            </multicast>
        </route>
    </camelContext>
</beans>

在我们的属性文件中,定义的值如下:

stop = true

在 Spring XML 文件中桥接 Camel 属性占位符

如果你使用的是 Spring Boot,则不适用此内容。这仅适用于使用 Spring XML 文件的旧版 Camel 和 Spring 应用程序。

Spring 框架不允许 Apache Camel 等第三方框架无缝接入 Spring 的属性占位符机制。不过,你可以通过声明一个类型为 org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer 的 Spring bean 来实现 Spring 与 Camel 的桥接,该类型是 Spring 的 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 类型。

要桥接 Spring 和 Camel,你必须按如下方式定义一个 bean:

Spring XML

<!-- bridge spring property placeholder with Camel -->
<!-- you must NOT use the <context:property-placeholder at the same time, only this bridge bean -->
<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
  <property name="location" value="classpath:org/apache/camel/component/properties/cheese.properties"/>
</bean>

你绝对不能同时使用 Spring 的 <context:property-placeholder> 命名空间,这在技术上是无法实现的。

声明该 Bean 之后,你就可以在 <camelContext> 标签中同时使用 Spring 风格和 Camel 风格来定义属性占位符,如下所示:

Spring XML

<!-- a bean that uses Spring property placeholder -->
<!-- the ${hi} is a spring property placeholder -->
<bean id="hello" class="org.apache.camel.component.properties.HelloBean">
  <property name="greeting" value="${hi}"/>
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <!-- in this route we use Camels property placeholder {{ }} style -->
  <route>
    <from uri="direct:{{cool.bar}}"/>
    <bean ref="hello"/>
    <to uri="{{cool.end}}"/>
  </route>
</camelContext>

注意到 hello bean 使用的是纯 Spring 属性占位符,采用 ${} 表示法;而在 Camel 路由中,我们使用 Camel 的占位符表示法 {{key}}。

属性占位符函数

Camel 支持内置函数,可用于从环境变量、系统属性、Kubernetes 以及自定义来源中查找属性。

详情请参阅属性占位符函数。

使用第三方属性来源

属性组件允许接入第三方来源,通过 camel-api 中的 PropertySource API 来加载和查找属性。

普通的 PropertySource 会按需查找属性,例如从数据库、HashiCorp Vault 等后端来源中获取值。

PropertySource 可以声明其支持一次性加载全部属性(通过实现 LoadablePropertiesSource),例如从文件系统中加载。这使得 Camel 属性组件能够在启动时一次性加载这些属性。

例如 camel-microprofile-config 组件就是这样实现的。当 Camel 启动时,会自动从 classpath 中发现第三方的 PropertySource。这是通过包含文件 META-INF/services/org/apache/camel/property-source-factory 实现的,该文件指向 PropertySource 实现类的全限定类名。

可参阅 MicroProfile Config 组件作为示例。

你也可以通过 Java API 在 PropertiesComponent 上注册第三方属性来源,如下所示:

PropertiesComponent pc = context.getPropertiesComponent();
pc.addPropertiesSource(myPropertySource);

评论

登录后参与评论

正在加载评论…