如何使用 Camel 属性占位符
属性占位符
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&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);评论
登录后参与评论
KnowForge