You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加多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();
    }
}

异常原因

  1. 组件注册与路由URI不匹配:你将队列名作为JMS组件的ID注册,但路由中直接使用队列名作为端点URI时,Camel默认会使用内置的jms组件,而非你注册的自定义组件。
  2. 多ConnectionFactory导致Spring注入失败:当容器中存在多个ConnectionFactory Bean且未标记@Primary时,Spring无法为Camel默认的jms组件自动选择一个ConnectionFactory,导致该组件的connectionFactory为null,最终触发connectionFactory must be specified异常。
  3. 单ConnectionFactory时的巧合正常:仅配置一个ConnectionFactory时,Spring会自动将其注入给默认jms组件,路由因此能正常工作,但这并非你预期的自定义组件逻辑。

解决方案

方案一:使用自定义组件ID并修正路由URI

  1. 修改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);
}
  1. 修正路由中的端点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,提问作者Роман

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 03:18:15