1. 概述

Apache Camel是一个强大的开源集成框架,实现了多种企业集成模式(EIP)。在消息路由场景中,我们经常需要根据消息内容执行不同的处理逻辑。为此,Camel提供了基于内容的路由器(Content Based Router)功能。

本文将深入探讨在Camel中实现条件路由的多种方式,帮助你在实际项目中灵活运用。

2. 依赖配置

首先在pom.xml中添加核心依赖:

<dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>

然后添加测试专用依赖:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-spring-junit5</artifactId>
    <version>4.3.0</version>
</dependency>

提示:测试依赖仅用于单元测试,生产环境可忽略。

3. 创建基础Camel应用

我们以一个简单的Spring Boot应用为例:

@SpringBootApplication
public class ConditionalRoutingSpringApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConditionalRoutingSpringApplication.class, args);
    }
}

这是标准的Spring Boot启动类,无需过多解释。

4. 条件路由机制

在Camel中,路由(Route)是基础构建块,通常由多个有序步骤组成,用于消费和处理消息。条件路由的核心在于choice()when()构造,这相当于Java中的if-else语句。

关键特点:

  • 支持多条件分支
  • 可通过消息体、消息头或自定义逻辑判断
  • 提供otherwise()作为默认分支

5. 基于消息体的路由

下面是一个基于消息内容的路由示例:

@Component
public class ConditionalBodyRouter extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("direct:start-conditional")
          .routeId("conditional-body-route")
          .choice()
            .when(body().contains("Baeldung"))
              .setBody(simple("Goodbye, Baeldung!"))
              .to("mock:result-body")
            .otherwise()
              .to("mock:result-body")
          .end();
    }
}

执行逻辑分解:

  1. direct:start-conditional接口接收消息
  2. 使用choice()开启条件判断块
  3. 当消息体包含"Baeldung"时:
    • 修改消息体为"Goodbye, Baeldung!"
    • 发送到mock:result-body接口
  4. 其他情况直接发送原始消息到mock接口
  5. end()关闭条件判断块

踩坑提示:忘记调用end()会导致路由构建失败,这是新手常见错误。

6. 单元测试验证

使用JUnit 5测试路由行为:

@SpringBootTest
@CamelSpringBootTest
class ConditionalBodyRouterUnitTest {
    @Autowired
    private ProducerTemplate template;

    @EndpointInject("mock:result-body")
    private MockEndpoint mock;

    @Test
    void whenSendBodyWithBaeldung_thenGoodbyeMessageReceivedSuccessfully() throws InterruptedException {
        mock.expectedBodiesReceived("Goodbye, Baeldung!");
        template.sendBody("direct:start-conditional", "Hello Baeldung Readers!");
        mock.assertIsSatisfied();
    }
}

测试流程:

  1. 设置mock接口预期接收的消息体
  2. direct:start-conditional发送包含"Baeldung"的消息
  3. 验证mock接口是否满足预期

简单粗暴:assertIsSatisfied()会抛出异常当预期未达成,无需手动断言。

7. 多样化条件判断

除了检查消息体,我们还可以基于消息头构建条件:

@Component
public class ConditionalHeaderRouter extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("direct:start-conditional-header")
            .routeId("conditional-header-route")
            .choice()
              .when(header("fruit").isEqualTo("Apple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                .to("mock:result")
            .end();
    }
}

关键变化:

  • 使用header("fruit")获取消息头值
  • 通过isEqualTo()进行字符串比较
  • 条件成立时设置新的消息头

技巧:Camel的Simple表达式语言(如simple("Apples"))可简化字符串操作。

8. 集成Java Bean

当需要复杂逻辑时,可调用Java方法:

public class FruitBean {
    public static boolean isApple(Exchange exchange) {
        return "Apple".equals(exchange.getIn().getHeader("fruit"));
    }
}

路由配置中使用Bean方法:

@Component
public class ConditionalBeanRouter extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("direct:start-conditional-bean")
            .routeId("conditional-bean-route")
            .choice()
              .when(method(FruitBean.class, "isApple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                .to("mock:result")
              .endChoice()
            .end();
    }
}

实现要点:

  1. 创建返回boolean的静态方法
  2. 方法接收Exchange参数获取消息上下文
  3. 使用method()绑定Bean方法
  4. 通过endChoice()显式结束条件分支(可选但推荐)

经验:复杂业务逻辑放在Bean中,路由配置保持简洁,便于维护。

9. 总结

本文系统介绍了Apache Camel中条件路由的实现方式:

  • 基础模式:使用choice()/when()/otherwise()构建条件分支
  • 判断依据:支持消息体、消息头、自定义Bean方法
  • 最佳实践:简单逻辑直接写在路由中,复杂逻辑委托给Bean

实际开发中,建议:

  1. 优先使用消息头判断(性能优于消息体解析)
  2. 复杂条件提取为Bean方法
  3. 始终编写单元测试验证路由逻辑

完整示例代码可参考GitHub仓库


原始标题:Apache Camel Conditional Routing