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

Spring通过代码Bean配置Azure Service Bus JMS多连接串报错问题

纯代码Bean配置Azure Service Bus JMS(无配置文件依赖、支持多连接串)

问题背景

当前使用如下Maven依赖实现Azure Service Bus主题的消息收发功能:

<dependency>
  <groupId>com.azure.spring</groupId>
  <artifactId>spring-cloud-azure-starter-servicebus-jms</artifactId>
  <version>4.2.0</version>
</dependency>

因业务需要配置多个连接字符串,要求通过Spring Bean代码形式完成配置,不依赖application.properties配置文件。最初编写的自定义AzureServiceBusJmsProperties配置Bean如下:

@Bean
@Primary
public AzureServiceBusJmsProperties priceListJmsProperties() {
    var properties = new AzureServiceBusJmsProperties();
    properties.setConnectionString(connectionString);
    properties.setPricingTier("standard");
    properties.setTopicClientId(priceListTopicName);
    return properties;
}

调试时发现该配置对象会被实例化两次:第一次加载了手动设置的正确配置值,第二次生成的实例所有配置字段均为null。由于AzureServiceBusJmsProperties内置参数校验逻辑要求必须指定对应配置项,抛出如下错误:

spring.jms.servicebus.connection-string' should be provided

尝试直接自定义ConnectionFactory实现配置,仍然出现相同报错。

报错根因

  • 4.2.0版本的starter内置ServiceBusJmsAutoConfiguration自动配置类,会默认绑定spring.jms.servicebus前缀的配置项,自动生成一个默认的AzureServiceBusJmsProperties实例。手动注册的@Primary Bean不会替换自动配置流程中生成的这个默认实例,无配置值的默认实例触发参数校验,就会抛出连接串缺失的错误。
  • 即使直接手动注册ConnectionFactory,自动配置类的校验逻辑会在自定义Bean加载前执行,依旧会触发相同报错。

实现方案

完全接管JMS相关Bean的初始化,跳过starter自带的自动配置流程即可,步骤如下:

  1. 排除starter自带的Service Bus JMS自动配置,避免默认空配置触发校验
// Spring Boot启动类上添加排除配置
@SpringBootApplication(exclude = {
    com.azure.spring.autoconfigure.jms.ServiceBusJmsAutoConfiguration.class,
    com.azure.spring.autoconfigure.jms.ServiceBusJmsPoolAutoConfiguration.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 编写配置类,手动注册多套配置属性、连接工厂、JmsTemplate、监听器容器工厂。以下是双连接串配置示例,不需要依赖spring.jms.servicebus前缀的配置项:
import com.azure.spring.autoconfigure.jms.AzureServiceBusJmsProperties;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;

import jakarta.jms.ConnectionFactory;

@Configuration
public class MultiServiceBusJmsConfig {
    // 若完全不需要读取配置文件,可直接将连接串、ClientID等值硬编码为常量
    private static final String PRICE_LIST_CONN_STR = "你的第一套Service Bus连接串";
    private static final String PRICE_LIST_CLIENT_ID = "price-list-topic-client";
    private static final String ORDER_CONN_STR = "你的第二套Service Bus连接串";
    private static final String ORDER_CLIENT_ID = "order-topic-client";

    // 构建第一套配置属性
    @Bean
    public AzureServiceBusJmsProperties priceListJmsProperties() {
        AzureServiceBusJmsProperties properties = new AzureServiceBusJmsProperties();
        properties.setConnectionString(PRICE_LIST_CONN_STR);
        properties.setPricingTier("standard");
        properties.setTopicClientId(PRICE_LIST_CLIENT_ID);
        return properties;
    }

    // 构建第二套配置属性
    @Bean
    public AzureServiceBusJmsProperties orderJmsProperties() {
        AzureServiceBusJmsProperties properties = new AzureServiceBusJmsProperties();
        properties.setConnectionString(ORDER_CONN_STR);
        properties.setPricingTier("standard");
        properties.setTopicClientId(ORDER_CLIENT_ID);
        return properties;
    }

    // 通用连接工厂构建方法
    private ConnectionFactory buildConnectionFactory(AzureServiceBusJmsProperties props) {
        String amqpUri = String.format("amqps://%s?amqp.idleTimeout=%d",
                props.getNamespace(),
                props.getIdleTimeout());
        JmsConnectionFactory factory = new JmsConnectionFactory(
                props.getUsername(),
                props.getPassword(),
                amqpUri
        );
        factory.setClientID(props.getTopicClientId());
        // 按需配置会话缓存,提升性能
        CachingConnectionFactory cachingFactory = new CachingConnectionFactory(factory);
        cachingFactory.setSessionCacheSize(10);
        return cachingFactory;
    }

    @Bean
    @Primary
    public ConnectionFactory priceListConnectionFactory() {
        return buildConnectionFactory(priceListJmsProperties());
    }

    @Bean
    public ConnectionFactory orderConnectionFactory() {
        return buildConnectionFactory(orderJmsProperties());
    }

    @Bean
    @Primary
    public JmsTemplate priceListJmsTemplate(ConnectionFactory priceListConnectionFactory) {
        return new JmsTemplate(priceListConnectionFactory);
    }

    @Bean
    public JmsTemplate orderJmsTemplate(ConnectionFactory orderConnectionFactory) {
        return new JmsTemplate(orderConnectionFactory);
    }

    @Bean
    @Primary
    public DefaultJmsListenerContainerFactory priceListJmsListenerFactory(
            ConnectionFactory priceListConnectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer
    ) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, priceListConnectionFactory);
        // 主题模式需开启pubSubDomain,队列模式可省略
        factory.setPubSubDomain(true);
        factory.setSessionTransacted(true);
        return factory;
    }

    @Bean
    public DefaultJmsListenerContainerFactory orderJmsListenerFactory(
            ConnectionFactory orderConnectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer
    ) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, orderConnectionFactory);
        factory.setPubSubDomain(true);
        factory.setSessionTransacted(true);
        return factory;
    }
}
  1. 业务使用时,按需指定对应的Bean即可,例如多主题监听示例:
// 注入第一套JmsTemplate发送消息
@Autowired
@Qualifier("priceListJmsTemplate")
private JmsTemplate priceListJmsTemplate;

// 监听第一套Service Bus的主题
@JmsListener(destination = "price-list-topic", containerFactory = "priceListJmsListenerFactory")
public void consumePriceListMsg(String payload) {
    // 业务处理逻辑
}

// 监听第二套Service Bus的主题
@JmsListener(destination = "order-topic", containerFactory = "orderJmsListenerFactory")
public void consumeOrderMsg(String payload) {
    // 业务处理逻辑
}

提示:如果使用Spring Boot 2.x版本,将上述代码中jakarta.jms.ConnectionFactory替换为javax.jms.ConnectionFactory即可,其余逻辑不变。

内容的提问来源于stack exchange,提问作者snake_404

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:27:32