1. 概述
本文将介绍 Apache Camel 并探索其核心概念之一——消息路由。首先我们会讲解基础概念和术语,然后展示两种定义路由的方式:Java DSL 和 Spring DSL。最后通过一个示例演示如何定义路由:从文件夹读取文件并移动到目标文件夹,同时为每个文件名添加日期前缀。
2. 关于 Apache Camel
Apache Camel 是一个开源集成框架,旨在简化系统集成。它允许用户使用统一的 API 集成各种系统,支持多种协议和数据类型,同时具备良好的扩展性,可引入自定义协议。
3. Maven 依赖
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.11</version>
</dependency>
测试所需依赖:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring-junit5</artifactId>
<version>4.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
4. 领域特定语言(DSL)
路由和路由引擎是 Camel 的核心部分。路由包含系统间集成的流程和逻辑。为了更简洁地定义路由,Camel 提供了多种领域特定语言(DSL):
- ✅ Java DSL:使用 Java/Groovy 代码定义路由
- ✅ Spring DSL:通过 XML 配置定义路由
两种 DSL 的选择主要取决于个人偏好,大部分功能都相互支持。Java DSL 提供更多 Spring DSL 不支持的功能,而 Spring DSL 的优势在于无需重新编译代码即可修改 XML 配置。
5. 核心概念与架构
5.1 核心概念
术语 | 说明 |
---|---|
Message | 包含传输数据的实体,由唯一标识符、消息体、头部和附件组成 |
Exchange | 消息的容器,在消费者接收消息时创建。支持单向消息和请求-响应交互模式 |
Endpoint | 系统接收/发送消息的通道(如 Web 服务 URI、队列、文件、邮箱等) |
Component | 终端工厂,为不同技术提供统一接口(Camel 已支持大量组件) |
Processor | 自定义集成逻辑的 Java 接口,包含 process() 方法处理消息 |
5.2 架构设计
Camel 架构分为两层:
- 顶层:
CamelContext
代表 Camel 运行时系统,连接路由、组件和终端 - 底层:
- 处理器负责终端间的路由和转换
- 终端负责集成不同系统
6. 定义路由
路由可通过 Java DSL 或 Spring DSL 定义。下面通过文件移动示例(添加日期前缀)演示两种方式。
6.1 使用 Java DSL
private static final long DURATION_MILIS = 10000;
private static final String SOURCE_FOLDER = "src/test/source-folder";
private static final String DESTINATION_FOLDER = "src/test/destination-folder";
@Test
public void givenJavaDSLRoute_whenCamelStart_thenMoveFolderContent() throws Exception {
CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("file://" + SOURCE_FOLDER + "?delete=true")
.process(new FileProcessor())
.to("file://" + DESTINATION_FOLDER);
}
});
camelContext.start();
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
File destinationFile1 = new File(DESTINATION_FOLDER + "/" + dateFormat.format(date) + "File1.txt");
File destinationFile2 = new File(DESTINATION_FOLDER + "/" + dateFormat.format(date) + "File2.txt");
Awaitility.await().atMost(DURATION_MILIS, TimeUnit.MILLISECONDS).untilAsserted(() -> {
assertThat(destinationFile1.exists()).isTrue();
assertThat(destinationFile2.exists()).isTrue();
});
camelContext.stop();
}
关键点解析:
configure()
方法流程:读取源文件夹 →FileProcessor
处理 → 发送到目标文件夹delete=true
表示处理成功后删除源文件camelContext.start()
启动 CamelAwaitility.await()
等待文件移动完成(最长等待DURATION_MILIS
毫秒)
文件名修改逻辑(FileProcessor
类):
@Component
public class FileProcessor implements Processor {
@Override
public void process(Exchange exchange) throws Exception {
String originalFileName = (String) exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String changedFileName = dateFormat.format(date) + originalFileName;
exchange.getIn().setHeader(Exchange.FILE_NAME, changedFileName);
}
}
⚠️ 通过操作 Exchange 的消息头实现文件名修改
6.2 使用 Spring DSL
Spring DSL 通过 XML 配置路由和处理器,实现完全的控制反转。这里展示混合使用 Spring DSL 和 Java DSL 的推荐方式:
XML 配置(camel-context-test.xml
):
<camelContext xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="fileRouter" />
</camelContext>
Java 路由定义(FileRouter
类):
@Component
public class FileRouter extends RouteBuilder {
private static final String SOURCE_FOLDER = "src/test/source-folder";
private static final String DESTINATION_FOLDER = "src/test/destination-folder";
@Override
public void configure() throws Exception {
from("file://" + SOURCE_FOLDER + "?delete=true")
.process(new FileProcessor())
.to("file://" + DESTINATION_FOLDER);
}
}
测试代码:
@Test
public void givenSpringDSLRoute_whenCamelStart_thenMoveFolderContent() throws Exception {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("camel-context-test.xml");
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
File destinationFile1 = new File(DESTINATION_FOLDER + "/" + dateFormat.format(date) + "File1.txt");
File destinationFile2 = new File(DESTINATION_FOLDER + "/" + dateFormat.format(date) + "File2.txt");
Awaitility.await().atMost(DURATION_MILIS, TimeUnit.MILLISECONDS).untilAsserted(() -> {
assertThat(destinationFile1.exists()).isTrue();
assertThat(destinationFile2.exists()).isTrue();
});
applicationContext.close();
}
✅ 这种方式结合了 Spring 的灵活性和 Java DSL 的强大功能
7. 总结
本文介绍了 Apache Camel 的基础概念和路由定义方式。通过文件移动示例可以看出,Camel 能让我们专注于业务逻辑,大幅减少样板代码。对于系统集成任务,Camel 是一个简单粗暴的解决方案。
本文代码可在 GitHub 获取