路由模板

路由模板 Bean 绑定

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

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

路由模板 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}}"

评论

登录后参与评论

正在加载评论…