添加多ConnectionFactory时出现connectionFactory must be specified异常排查
问题:Apache Camel集成多IBM MQ ConnectionFactory时路由启动失败
问题现象
仅配置一个ConnectionFactory时系统正常运行,添加第二个ConnectionFactory后,路由FromSAA启动失败,抛出connectionFactory must be specified异常。
异常堆栈
org.apache.camel.FailedToStartRouteException: Failed to start route FromSAA because of null at org.apache.camel.impl.engine.RouteService.setUp(RouteService.java:132) at org.apache.camel.impl.engine.InternalRouteStartupManager.doInitRoutes(InternalRouteStartupManager.java:92) at org.apache.camel.impl.engine.AbstractCamelContext.doInit(AbstractCamelContext.java:2949) at org.apache.camel.support.service.BaseService.init(BaseService.java:83) at org.apache.camel.impl.engine.AbstractCamelContext.init(AbstractCamelContext.java:2630) at org.apache.camel.support.service.BaseService.start(BaseService.java:111) at org.apache.camel.impl.engine.AbstractCamelContext.start(AbstractCamelContext.java:2649) at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:262) at org.apache.camel.spring.SpringCamelContext.start(SpringCamelContext.java:119) at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:151) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:938) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) at ru.rbru.fms.router.Bootstrap.main(Bootstrap.java:13) Caused by: java.lang.IllegalArgumentException: connectionFactory must be specified at org.apache.camel.util.ObjectHelper.notNull(ObjectHelper.java:156) at org.apache.camel.component.jms.JmsConfiguration.createConnectionFactory(JmsConfiguration.java:1746) at org.apache.camel.component.jms.JmsConfiguration.getOrCreateConnectionFactory(JmsConfiguration.java:856) at org.apache.camel.component.jms.JmsConfiguration.createListenerConnectionFactory(JmsConfiguration.java:1754) at org.apache.camel.component.jms.JmsConfiguration.getOrCreateListenerConnectionFactory(JmsConfiguration.java:900) at org.apache.camel.component.jms.JmsConfiguration.configureMessageListenerContainer(JmsConfiguration.java:1582) at org.apache.camel.component.jms.JmsConfiguration.createMessageListenerContainer(JmsConfiguration.java:786) at org.apache.camel.component.jms.JmsEndpoint.createMessageListenerContainer(JmsEndpoint.java:157) at org.apache.camel.component.jms.JmsEndpoint.createConsumer(JmsEndpoint.java:152) at org.apache.camel.component.jms.JmsEndpoint.createConsumer(JmsEndpoint.java:69) at org.apache.camel.impl.engine.DefaultRoute.gatherRootServices(DefaultRoute.java:636) at org.apache.camel.impl.engine.DefaultRoute.gatherServices(DefaultRoute.java:620) at org.apache.camel.impl.engine.DefaultRoute.initializeServices(DefaultRoute.java:205) at org.apache.camel.impl.engine.RouteService.doSetup(RouteService.java:151) at org.apache.camel.impl.engine.RouteService.setUp(RouteService.java:130) ... 23 common frames omitted
相关代码
IbmMqInitializer类
@Component @RequiredArgsConstructor public class IbmMqInitializer { private static final String ACKNOWLEDGEMENT_MODE = "CLIENT_ACKNOWLEDGE"; public void configure(CamelContext context, IbmMqProperties ibmMqProperties, ConnectionFactory connectionFactory) throws JMSException { registerIbmMq(context, ibmMqProperties, connectionFactory); } private void registerIbmMq(CamelContext context, IbmMqProperties ibmMqProperties, ConnectionFactory connectionFactory) { registerComponent(context, connectionFactory, ibmMqProperties); } private void registerComponent(CamelContext context, ConnectionFactory connectionFactory, IbmMqProperties ibmMqProperties) { ComponentsBuilderFactory.jms() .connectionFactory(connectionFactory) .acknowledgementModeName(ACKNOWLEDGEMENT_MODE) .concurrentConsumers(ibmMqProperties.getConcurrentConsumersCount()) .register(context, ibmMqProperties.getQueue()); } }
ApplicationConfig类
@Configuration public class ApplicationConfig { private static final Integer CCSID_UTF_8 = 1208; @Bean @ConfigurationProperties("resources.ibm.mq.saa") public IbmMqProperties saaIbmMqProps() { return new IbmMqProperties(); } @Bean @ConfigurationProperties("resources.ibm.mq.esb") public IbmMqProperties esbIbmMqProps() { return new IbmMqProperties(); } @Bean public ConnectionFactory saaIbmMqFactory(@Qualifier("saaIbmMqProps") IbmMqProperties ibmMqProperties) throws JMSException { return getMqQueueConnectionFactory(ibmMqProperties); } @Bean public ConnectionFactory esbIbmMqFactory(@Qualifier("esbIbmMqProps") IbmMqProperties ibmMqProperties) throws JMSException { return getMqQueueConnectionFactory(ibmMqProperties); } private ConnectionFactory getMqQueueConnectionFactory(IbmMqProperties ibmMqProperties) throws JMSException { var factory = new MQQueueConnectionFactory(); factory.setCCSID(CCSID_UTF_8); factory.setTransportType(CommonConstants.WMQ_CM_CLIENT); factory.setHostName(ibmMqProperties.getHostName()); factory.setPort(ibmMqProperties.getPort()); factory.setChannel(ibmMqProperties.getChannel()); factory.setQueueManager(ibmMqProperties.getQueueManager()); return factory; } }
MqConfiguration类
@Configuration @RequiredArgsConstructor @Slf4j public class MqConfiguration { private final IbmMqInitializer ibmMqInitializer; @Bean public CamelContextConfiguration contextConfiguration(@Qualifier("saaIbmMqProps") IbmMqProperties saaIbmMqProps, @Qualifier("saaIbmMqFactory") ConnectionFactory saaIbmMqFactory, @Qualifier("esbIbmMqProps") IbmMqProperties esbIbmMqProps, @Qualifier("esbIbmMqFactory") ConnectionFactory esbIbmMqFactory) { return new CamelContextConfiguration() { @Override public void beforeApplicationStart(CamelContext context) { try { ibmMqInitializer.configure(context, saaIbmMqProps, saaIbmMqFactory); ibmMqInitializer.configure(context, esbIbmMqProps, esbIbmMqFactory); } catch (JMSException e) { log.error("Failed to connect to MQ", e); } } @Override public void afterApplicationStart(CamelContext camelContext) { } }; } }
SaaRoute路由类
@Service public class SaaRoute extends RouteBuilder { public static final String STATUS = "status"; private final SaaProcessor processor; private final IbmMqProperties saaIbmMqProperties; private final IbmMqProperties esbIbmMqProperties; public SaaRoute(SaaProcessor processor, @Qualifier("saaIbmMqProps") IbmMqProperties saaIbmMqProperties, @Qualifier("esbIbmMqProps") IbmMqProperties esbIbmMqProperties) { this.processor = processor; this.saaIbmMqProperties = saaIbmMqProperties; this.esbIbmMqProperties = esbIbmMqProperties; } @Override public void configure() { from(saaIbmMqProperties.getQueue()).routeId("FromSAA") .log(LoggingLevel.DEBUG, log, "New message received") .process(processor) .choice() .when(exchange -> exchange.getProperty(STATUS).equals(MessageStatus.SUCCESSFULLY_PARSED)) .to(esbIbmMqProperties.getQueue()) .otherwise() .log("Message was not converted") .endChoice(); } }
异常原因
- 组件注册与路由URI不匹配:你将队列名作为JMS组件的ID注册,但路由中直接使用队列名作为端点URI时,Camel默认会使用内置的
jms组件,而非你注册的自定义组件。 - 多ConnectionFactory导致Spring注入失败:当容器中存在多个
ConnectionFactoryBean且未标记@Primary时,Spring无法为Camel默认的jms组件自动选择一个ConnectionFactory,导致该组件的connectionFactory为null,最终触发connectionFactory must be specified异常。 - 单ConnectionFactory时的巧合正常:仅配置一个ConnectionFactory时,Spring会自动将其注入给默认
jms组件,路由因此能正常工作,但这并非你预期的自定义组件逻辑。
解决方案
方案一:使用自定义组件ID并修正路由URI
- 修改
IbmMqInitializer的组件注册逻辑,使用唯一的组件ID(如jms-saa、jms-esb),而非队列名:
private void registerComponent(CamelContext context, ConnectionFactory connectionFactory, IbmMqProperties ibmMqProperties) { // 按规则生成唯一组件ID,比如结合队列管理器名称 String componentId = "jms-" + ibmMqProperties.getQueueManager(); ComponentsBuilderFactory.jms() .connectionFactory(connectionFactory) .acknowledgementModeName(ACKNOWLEDGEMENT_MODE) .concurrentConsumers(ibmMqProperties.getConcurrentConsumersCount()) .register(context, componentId); }
- 修正路由中的端点URI,指定自定义组件ID:
@Override public void configure() { // 格式:组件ID:queue:队列名 from("jms-saa:queue:" + saaIbmMqProperties.getQueue()).routeId("FromSAA") .log(LoggingLevel.DEBUG, log, "New message received") .process(processor) .choice() .when(exchange -> exchange.getProperty(STATUS).equals(MessageStatus.SUCCESSFULLY_PARSED)) .to("jms-esb:queue:" + esbIbmMqProperties.getQueue()) .otherwise() .log("Message was not converted") .endChoice(); }
方案二:为默认jms组件指定Primary ConnectionFactory
如果你想继续使用默认jms组件,只需为其中一个ConnectionFactory添加@Primary注解,让Spring明确注入对象:
@Bean @Primary // 添加此注解指定默认注入的ConnectionFactory public ConnectionFactory saaIbmMqFactory(@Qualifier("saaIbmMqProps") IbmMqProperties ibmMqProperties) throws JMSException { return getMqQueueConnectionFactory(ibmMqProperties); }
此方案仅适用于默认组件只需一个ConnectionFactory的场景,若两个队列需要不同的ConnectionFactory配置,建议使用方案一。
内容的提问来源于stack exchange,提问作者Роман
相关产品推荐
相关产品推荐

