
当迁移Apache Camel应用程序从2.x到3.x时,特别是那些利用Spring XML进行配置并需要持续运行的场景,`Main`类的使用方式会带来挑战。本文旨在解决如何正确运行一个集成Spring XML上下文的持久化Camel 3.x应用程序,重点阐述了`org.apache.camel.main.Main`与更适合此类场景的`org.apache.camel.spring.Main`(来自`camel-spring-main`构件)之间的关键区别。
1. 迁移挑战:Camel 2.x 到 3.x 的 Main 类与 Spring XML 集成
在Apache Camel 2.x版本中,开发者通常使用 org.apache.camel.spring.Main 类来启动一个长时运行的Camel应用程序,并同时加载Spring XML配置文件以管理Bean和配置。这种方式使得Camel路由能够无缝地利用Spring容器中定义的各种服务和属性。典型的启动代码示例如下:
package com.foo.email.ffdb.listener;
import java.util.Properties;
import javax.annotation.Resource;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.Main; // Camel 2.x 使用的 Main 类
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
// ... 其他导入
public class EmailDBListener extends RouteBuilder {
private static Logger log = LogManager.getLogger(EmailDBListener.class.getName());
private static String routeId = "EMAIL_ROUTE_ID"; // 简化常量
@Autowired
private EmailDBProcessor emaiDBProcessor;
@Resource
private Properties emailProperties;
public static void main(String[] args) throws Exception {
System.out.println("STARTING EMAILDBLISTENER");
log.debug("Starting Email Batch ");
Main main = new Main();
main.setApplicationContextUri("app-context.xml"); // 加载Spring XML上下文
main.run(); // 启动并保持运行
log.info("Email Batch Started:"); // 这行代码通常在 main.run() 阻塞后不会立即执行
}
@Override
public void configure() throws Exception {
log.debug("configure() ");
from(configureSqlTimer())
.routeId(routeId)
.to("sqlComponent:{{SQL.READ_EMAIL_REQUESTS}}")
.bean("fireForgetServiceMapper", "readEmailRequests")
.process(emaiDBProcessor);
}
private String configureSqlTimer() {
log.debug("configureSqlTimer() ");
String pollingTime = emailProperties.getProperty("POLLING_TIME"); // 简化常量
String sqlTimer = "timer://pollFireFrgtTable?period=" + pollingTime + "s";
return sqlTimer;
}
}在迁移到Camel 3.x时,开发者可能会发现原有的org.apache.camel.spring.Main类似乎不再可用,或者在新的org.apache.camel.main.Main类中找不到setApplicationContextUri这样的方法,这导致了Spring XML配置无法加载,且应用程序无法保持持续运行。
2. Camel 3.x Main 类的区分
Camel 3.x引入了模块化和更清晰的职责划分,导致存在两个主要的Main类,它们服务于不同的场景:
org.apache.camel.main.Main (来自 camel-main 构件): 这个Main类主要用于启动独立的Camel应用程序,通常用于纯Java DSL路由或不依赖复杂Spring上下文的场景。它提供了一种轻量级的启动方式,但默认情况下不直接支持通过URI加载Spring XML配置文件。它的设计更偏向于独立运行,或者与Spring Boot等框架进行集成,而不是直接加载传统的Spring XML。
org.apache.camel.spring.Main (来自 camel-spring-main 构件): 这是解决上述迁移问题的关键。这个Main类是专门为与Spring框架深度集成而设计的,特别是当你的应用程序需要加载Spring XML配置文件(例如app-context.xml)来定义Bean、属性源甚至Camel路由时。它保留了setApplicationContextUri等方法,能够将CamelContext与Spring ApplicationContext绑定,从而允许Camel路由访问Spring管理的Bean。
3. 解决方案:利用 camel-spring-main 构件
对于需要加载Spring XML上下文并保持Camel应用程序持续运行的Camel 3.x项目,正确的做法是使用org.apache.camel.spring.Main类。这个类提供了与Camel 2.x相似的API,使得迁移过程更为顺畅。
3.1. 更新后的代码示例
以下是Camel 3.x中如何使用org.apache.camel.spring.Main来启动应用程序的示例:
package com.foo.email.ffdb.listener;
import java.util.Properties;
import javax.annotation.Resource;
import org.apache.camel.builder.RouteBuilder;
// 关键:确保导入的是 org.apache.camel.spring.Main
import org.apache.camel.spring.Main;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
// ... 其他导入
public class EmailDBListener extends RouteBuilder {
private static final Logger log = LogManager.getLogger(EmailDBListener.class.getName());
private static final String routeId = "EMAIL_ROUTE_ID"; // 简化常量
@Autowired
private EmailDBProcessor emaiDBProcessor; // 假设这是一个Spring Bean
@Resource
private Properties emailProperties; // 假设这是一个Spring Bean
public static void main(String[] args) throws Exception {
System.out.println("STARTING EMAILDBLISTENER");
log.debug("Starting Email Batch ");
// 在Camel 3.x中,用于Spring XML集成的正确Main类
Main main = new Main();
main.setApplicationContextUri("app-context.xml"); // 加载Spring XML上下文
main.run(); // 启动并保持应用程序运行,直到接收到停止信号
log.info("Email Batch Started:"); // 这行代码在应用程序运行期间不会被执行
}
@Override
public void configure() throws Exception {
log.debug("configure() ");
from(configureSqlTimer())
.routeId(routeId)
.to("sqlComponent:{{SQL.READ_EMAIL_REQUESTS}}")
.bean("fireForgetServiceMapper", "readEmailRequests") // fireForgetServiceMapper 假设是Spring Bean
.process(emaiDBProcessor); // emaiDBProcessor 假设是Spring Bean
}
private String configureSqlTimer() {
log.debug("configureSqlTimer() ");
String pollingTime = emailProperties.getProperty("POLLING_TIME"); // 简化常量
String sqlTimer = "timer://pollFireFrgtTable?period=" + pollingTime + "s";
return sqlTimer;
}
}3.2. Maven 依赖配置
为了使用org.apache.camel.spring.Main,你需要在项目的pom.xml中添加camel-spring-main依赖。同时,确保你的Camel版本与Spring版本兼容。
org.apache.camel camel-spring-main 3.14.0 org.apache.camel camel-sql 3.14.0 org.apache.camel camel-timer 3.14.0 org.springframework spring-context 5.3.14 org.apache.logging.log4j log4j-api 2.17.1 org.apache.logging.log4j log4j-core 2.17.1
4. 关键注意事项和最佳实践
- Classpath 管理: 确保你的app-context.xml文件位于项目的classpath中,通常放置在src/main/resources目录下。
- 持续运行: main.run()方法是一个阻塞调用。它会启动CamelContext并保持JVM运行,直到接收到外部停止信号(如Ctrl+C或系统关闭事件)。这意味着在main.run()之后的代码通常不会立即执行,而是在应用程序关闭时才可能被触及。
- Spring Bean 注入: camel-spring-main会负责初始化Spring ApplicationContext,并将其绑定到CamelContext。这样,你在Spring XML中定义的Bean就可以通过@Autowired、@Resource或在Camel路由中通过Bean名称引用。
- Spring Boot 替代方案: 对于全新的项目,或者计划进行更深层次的Spring集成,Spring Boot提供了更现代、更简化的方式来构建和运行Camel应用程序。Spring Boot Starter for Camel能够自动配置和管理CamelContext,并与Spring的自动配置机制无缝集成。然而,对于不使用Spring Boot的现有项目,camel-spring-main是直接且有效的解决方案。
-
故障排除: 如果应用程序未能启动,或者Spring Bean未正确注入,请检查以下几点:
- app-context.xml的路径是否正确。
- Spring版本与Camel版本之间是否存在兼容性问题。
- 所有必需的Camel组件和Spring依赖是否已添加到pom.xml中。
- 检查日志输出,通常能提供有价值的错误信息。
总结
在将Apache Camel应用程序从2.x迁移到3.x,并需要利用Spring XML上下文进行配置和实现长时运行的场景中,选择正确的Main类至关重要。通过使用camel-spring-main构件中的org.apache.camel.spring.Main类,开发者可以轻松地加载Spring XML配置,并确保Camel应用程序能够持续、稳定地运行,从而平滑地完成升级过程。










