路由模板 Bean 绑定
路由模板 Bean 绑定
路由模板允许绑定局部作用域的 Bean,这些 Bean 仅在基于模板创建路由的过程中使用。这样就可以复用同一个模板来创建多条路由,而其中的 Bean 对每条创建的路由来说都是局部(私有)的。
例如,给定下面的路由模板,我们使用 templateBean 来设置局部 Bean,如示所示:
纯 Java:带 lambda supplier 的 templateBean
routeTemplate("s3template")
.templateParameter("region")
.templateParameter("bucket")
.templateBean("myClient", S3Client.class, rtc ->
S3Client.builder().region(rtc.getProperty("region", Region.class)).build();
)
.from("direct:s3-store")
// must refer to the bean with {{myClient}}
.to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}")该模板有两个参数,用于指定 AWS 区域和 S3 存储桶。要连接到 S3,需要一个 software.amazon.awssdk.services.s3.S3Client bean。
要创建这个 bean,我们通过 templateBean DSL 来指定,并将 bean 的 id 设为 myClient。bean 的类型可以指定(S3Client.class),但这是可选的,当你需要按类型而非按名称来查找 bean 时可以使用。
为了确保创建 bean 的代码在稍后执行(即 Camel 根据模板创建路由时),该代码必须以 supplier 的形式提供。由于我们希望在创建 bean 期间能够访问模板参数,因此使用 Camel 的 BeanSupplier,它提供了对 RouteTemplateContext 的访问,也就是上面代码中的 rtc 变量。
id 为 myClient 的本地 bean 必须使用 Camel 的属性占位符语法来引用,例如在路由模板中使用 {{myClient}},如上面的 to 端点所示。这是因为本地 bean 必须是唯一的,Camel 会在内部重新分配 bean id,使用唯一的 id 来替代 myClient。而这是借助属性占位符功能来完成的。 |
|---|
如果基于该模板创建了多条路由,那么每条创建的路由都会拥有各自的 S3Client bean。
从模板构建器向路由模板绑定 bean
TemplatedRouteBuilder 同样允许在基于已有模板创建路由时绑定本地 bean(即可指定这些 bean)。
假设下面的路由模板是用 XML 定义的:
仅 XML:未绑定 bean 的路由模板
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
<route>
<from uri="direct:s3-store"/>
<to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/>
</route>
</routeTemplate>模板中没有为 #{{myClient}} 配置 bean 绑定,而创建该模板需要这些绑定。
当通过 TemplatedRouteBuilder 基于模板创建路由时,如果你希望该 bean 作用于本地范围(不与他人共享),则可以提供 bean 绑定:
- Java
- Java DSL
- XML
- YAML
TemplatedRouteBuilder.builder(context, "s3template")
.parameter("region", "US-EAST-1")
.parameter("bucket", "myBucket")
.bean("myClient", S3Client.class,
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build())
.routeId("mys3route")
.add();可以看出,这里的绑定方式与在路由模板中直接使用 templateBean 时类似。
templatedRoute("s3template")
.parameter("region", "US-EAST-1")
.parameter("bucket", "myBucket")
.bean("myClient", S3Client.class,
rtc -> S3Client.builder() (1)
.region(rtc.getProperty("region", Region.class))
.build())
.routeId("mys3route");| 1 | 注意,bean 方法的第三个参数并非直接是 bean,而是用于创建该 bean 的工厂方法,这里我们使用 lambda 表达式作为工厂方法。 |
|---|
<templatedRoute routeTemplateRef="s3template" routeId="mys3route">
<parameter name="region" value="US-EAST-1"/>
<parameter name="bucket" value="myBucket"/>
<bean name="myClient" type="software.amazon.awssdk.services.s3.S3Client"
scriptLanguage="groovy"> (1)
<script>
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()
</script>
</bean>
</templatedRoute>| 1 | 对于非 Java DSL,如果 Bean 工厂比较复杂,你仍然可以借助 groovy 之类的语言,在 script 元素中定义 Bean 工厂。 |
|---|
- templatedRoute:
routeTemplateRef: "s3template"
routeId: "mys3route"
parameters:
- name: "region"
value: "US-EAST-1"
- name: "bucket"
value: "myBucket"
beans:
- name: "myClient"
type: "software.amazon.awssdk.services.s3.S3Client"
scriptLanguage: "groovy"
script: | (1)
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()| 1 | 对于非 Java DSL,在处理复杂的 bean 工厂时,你仍然可以借助 groovy 之类的语言,将 script 键的值定义为你的 bean 工厂。 |
|---|
除了从模板构建器绑定 bean 之外,你也可以在模板之外创建该 bean,然后按引用进行绑定。
- Java
- Java DSL
final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build();
TemplatedRouteBuilder.builder(context, "s3template")
.parameter("region", Region.US_EAST_1)
.parameter("bucket", "myBucket")
.bean("myClient", myClient)
.routeId("mys3route")
.add();final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build();
templatedRoute("s3template")
.parameter("region", "US-EAST-1")
.parameter("bucket", "myBucket")
.bean("myClient", S3Client.class, rtc -> myClient)
.routeId("mys3route");| 应尽可能直接在模板内部创建本地 Bean(如果可能),因为这样可以确保路由模板开箱即用。否则,每次从路由模板创建新路由时,都必须创建或提供该 Bean。不过,后一种方式允许以任何其他自定义方式创建 Bean。 |
|---|
使用 Bean 类型将 Bean 绑定到路由模板
你可以通过引用全限定类名来创建本地 Bean,Camel 将使用该类创建新的本地 Bean 实例。使用此方式时,创建的 Bean 是通过类的默认构造函数创建的。
Bean 实例可以通过 getter/setter 方式配置属性。前面创建 AWS S3Client 的示例不支持这种方式,因为它使用的是流式构建器模式(而非 getter/setter)。
| 从 Camel 4.6 起,Bean 也支持构造函数参数 |
|---|
假设我们有如下类:
仅 Java:带有 getter/setter 属性的 Bean 类
public class MyBar {
private String name;
private String address;
// getter/setter omitted
public String location() {
return "The bar " + name + " is located at " + address;
}
}然后,我们可以在路由模板中将 MyBar 类作为本地 Bean 使用,方式如下:
- Java
- XML
- YAML
routeTemplate("barTemplate")
.templateParameter("bar")
.templateParameter("street")
.templateBean("myBar")
.typeClass("com.foo.MyBar")
.property("name", "{{bar}}")
.property("address", "{{street}}")
.end()
.from("direct:going-out")
.to("bean:{{myBar}}");使用 Java DSL 时,你还可以在 typeClass 中以类型安全的方式引用 Bean 类,只需按如下方式引用 .class 即可:
routeTemplate("barTemplate")
.templateParameter("bar")
.templateParameter("street")
.templateBean("myBar")
.typeClass(MyBar.class)
.property("name", "{{bar}}")
.property("address", "{{street}}")
.end()
.from("direct:going-out")
.to("bean:{{myBar}}");在 XML 中,我们使用 #class: 语法来指定类的完全限定名(FQN),如下所示:
<routeTemplate id="myBar">
<templateParameter name="bar"/>
<templateParameter name="street"/>
<templateBean name="myBean" type="#class:com.foo.MyBar">
<properties>
<property key="name" value="{{bar}}"/>
<property key="address" value="{{street}}"/>
</properties>
</templateBean>
<route>
<from uri="direct:going-out"/>
<to uri="bean:{{myBar}}"/>
</route>
</routeTemplate>在 YAML 中,我们使用 #class: 语法来指定类的全限定名(FQN),如下所示:
- routeTemplate:
id: "myBar"
parameters:
- name: "bar"
- name: "street"
beans:
- name: "myBean"
type: "#class:com.foo.MyBar"
properties:
name: "{{bar}}"
address: "{{street}}"
from:
uri: direct:going-out
steps:
- to:
uri: "bean:{{myBar}}"使用脚本语言绑定 Bean 到路由模板(高级)
你可以使用 Groovy、Java 等脚本语言来创建 Bean。这样就能使用 Camel 所支持的脚本语言(例如 Groovy)来定义路由模板。
例如,创建 AWS S3 客户端可以按照 Java 中所示的方式完成(其中内联了 Groovy 代码):
- Java
- XML
- YAML
routeTemplate("s3template")
.templateParameter("region")
.templateParameter("bucket")
.templateBean("myClient", "groovy",
"software.amazon.awssdk.services.s3.S3Client.S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()"
)
.from("direct:s3-store")
// must refer to the bean with {{myClient}}
.to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}")注意,Groovy 代码同样可以直接内联到 XML 的路由模板中。
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
<templateBean name="myClient" scriptLanguage="groovy">
<script>
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()
</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
<to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/>
</route>
</routeTemplate>注意,Groovy 代码同样可以直接内联到 DSL 的路由模板中。
- routeTemplate:
id: "s3template"
parameters:
- name: "region"
- name: "bucket"
beans:
- name: "myClient"
type: "software.amazon.awssdk.services.s3.S3Client"
scriptLanguage: "groovy"
script: |
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()
from:
uri: direct:s3-store
steps:
- to:
uri: "aws2-s3:{{bucket}}"
parameters:
amazonS3Client: "#{{myClient}}"Groovy 代码可以通过使用 resource: 作为前缀,外置到类路径或文件系统中的文件里,例如:
- Java
- XML
- YAML
routeTemplate("s3template")
.templateParameter("region")
.templateParameter("bucket")
.templateBean("myClient", "groovy", "resource:classpath:s3bean.groovy")
.from("direct:s3-store")
// must refer to the bean with {{myClient}}
.to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}")注意我们如何告诉 Camel 该 Bean 的类型是 software.amazon.awssdk.services.s3.S3Client,这有助于 Camel 更好地理解它,同时也使该 Bean 能够通过类型进行自动装配和绑定。type 是可选的,可以省略。例如在这个例子中,我们通过 Bean 的 id myClient 来注入该 Bean。
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
<templateBean name="myClient" scriptLanguage="groovy"
type="software.amazon.awssdk.services.s3.S3Client">
<script>resource:classpath:s3bean.groovy</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
<to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/>
</route>
</routeTemplate>请注意,我们可以告诉 Camel 该 Bean 的类型是 software.amazon.awssdk.services.s3.S3Client,这能让 Camel 更好地理解该 Bean,同时也使该 Bean 能够通过类型进行自动装配和绑定。type 是可选的,可以省略。例如,在本例中,我们是通过 Bean 的 id myClient 来注入该 Bean 的。
- routeTemplate:
id: "s3template"
parameters:
- name: "region"
- name: "bucket"
beans:
- name: "myClient"
scriptLanguage: "groovy"
type: "software.amazon.awssdk.services.s3.S3Client"
script: "resource:classpath:s3bean.groovy"
from:
uri: direct:s3-store
steps:
- to:
uri: "aws2-s3:{{bucket}}"
parameters:
amazonS3Client: "#{{myClient}}"然后在类路径根目录下创建文件 s3bean.groovy:
s3bean.groovy
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()你可以通过 scriptLanguage 指定的支持语言如下:
| 类型 | 描述 |
|---|---|
| bean | 调用 Java 类上的方法来创建 Bean。 |
| groovy | 使用 Groovy 脚本创建 Bean。 |
| java | 使用 Java 代码创建 Bean,该代码在运行时通过 jOOR 库编译。 |
| mvel | 使用 MVEL 模板脚本创建 Bean。 |
| ognl | 使用 OGNL 模板脚本创建 Bean。 |
| name | 使用给定 name 指定的第三方语言创建 Bean。 |
在计算脚本时,Camel 会将 RouteTemplateContext 作为根对象绑定,并命名为 rtc。这意味着你可以通过 rtc 访问 RouteTemplateContext 和 CamelContext 中的全部信息。
这正是我们在前面示例的脚本中所做的,我们通过以下方式获取模板参数:
rtc.getProperty('region', String.class)要访问 CamelContext,可以这样做:
var cn = rtc.getCamelContext().getName()功能最强大的语言是 Groovy 和 Java。其他语言的灵活性有限,因为它们不是完整的编程语言,更适合用于模板化需求。
如果创建本地 Bean 需要编写代码,且路由模板不是使用 Java 代码定义的,建议使用 Groovy 或 Java。
如果从现有的 Java 方法(静态或非静态方法)创建本地 Bean,且路由模板不是使用 Java 代码定义的,则可以使用 Bean 语言。
例如,假设有一个名为 com.foo.MyAwsHelper 的类,其中包含一个名为 createS3Client 的方法,那么你可以在路由模板中按如下方式调用该方法:
- Java
- XML
- YAML
routeTemplate("s3template")
.templateParameter("region")
.templateParameter("bucket")
.templateBean("myClient", "beam", "com.foo.MyAwsHelper?method=createS3Client")
.from("direct:s3-store")
// must refer to the bean with {{myClient}}
.to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") <routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
<templateBean name="myClient" type="bean">
<script>com.foo.MyAwsHelper?method=createS3Client</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
<to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/>
</route>
</routeTemplate>- routeTemplate:
id: "s3template"
parameters:
- name: "region"
- name: "bucket"
beans:
- name: "myClient"
type: "software.amazon.awssdk.services.s3.S3Client"
scriptLanguage: "bean"
script: "com.foo.MyAwsHelper?method=createS3Client"
from:
uri: direct:s3-store
steps:
- to:
uri: "aws2-s3:{{bucket}}"
parameters:
amazonS3Client: "#{{myClient}}"createS3Client 方法的方法签名必须包含一个 RouteTemplateContext 参数,如下所示:
仅限 Java:bean 工厂方法签名
public static S3Client createS3Client(RouteTemplateContext rtc) {
return S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build();
}如果你使用的是纯 Java 代码(模板和创建本地 Bean 都是),那么可以按照前文所述,用 Java lambda 风格创建本地 Bean。
配置所创建 Bean 的类型
必须设置 type,以定义所创建 Bean 的完全限定类名(FQN)。
在下面的路由模板中,我们要告诉 Camel 该 Bean 的类型是 software.amazon.awssdk.services.s3.S3Client。
- Java
- XML
- YAML
在 Java DSL 中,你可以通过类来引用该类型(即 S3Client.java,这是一种类型安全的方式:
routeTemplate("s3template")
.templateParameter("region")
.templateParameter("bucket")
.templateBean("myClient", S3Client.class, "bean", "com.foo.MyAwsHelper?method=createS3Client")
.from("direct:s3-store")
// must refer to the bean with {{myClient}}
.to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") <routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
<templateBean name="myClient" scriptLanguage="bean"
type="software.amazon.awssdk.services.s3.S3Client">
<script>com.foo.MyAwsHelper?method=createS3Client</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
<to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/>
</route>
</routeTemplate>- routeTemplate:
id: "s3template"
parameters:
- name: "region"
- name: "bucket"
beans:
- name: "myClient"
scriptLanguage: "bean"
type: "software.amazon.awssdk.services.s3.S3Client"
script: "com.foo.MyAwsHelper?method=createS3Client"
from:
uri: direct:s3-store
steps:
- to:
uri: "aws2-s3:{{bucket}}"
parameters:
amazonS3Client: "#{{myClient}}"评论
登录后参与评论
KnowForge