Spring Boot使用IBM MQ发送消息到Topic失败问题及解决
问题场景
尝试通过JmsTemplate实现将消息发送到IBM MQ的Topic而非队列,配置运行后触发如下错误:
com.ibm.msg.client.jakarta.jms.DetailedIllegalStateException: JMSCC1112: The operation for a domain specific object was not valid. The operation 'createTopic' is not valid for type 'QueueSession'.
A JMS application attempted to perform an operation on domain specific object, but the operation is valid only for the other messaging domain.
原配置与代码
1. MQ配置类
@Configuration @EnableJms public class MQConfiguration { @Value("${notification.topic.name}") String notificationTopicName; @Bean @ConfigurationProperties(prefix = "jms-mq.notification") public MQConfigurationProperties mqConfigurationProperties() { return new MQConfigurationProperties(); } @Bean public MQQueueConnectionFactory mqQueueConnectionFactory() { MQConnectionFactoryFactory mqConnectionFactoryFactory = new MQConnectionFactoryFactory(mqConfigurationProperties(), null); MQQueueConnectionFactory mqQueueConnectionFactory = mqConnectionFactoryFactory.createConnectionFactory(MQQueueConnectionFactory.class); return mqQueueConnectionFactory; } @Bean public JmsListenerContainerFactory<?> jmsListenerContainerFactory(DefaultJmsListenerContainerFactoryConfigurer configurer){ DefaultJmsListenerContainerFactory defaultJmsListenerContainerFactory=new DefaultJmsListenerContainerFactory(); defaultJmsListenerContainerFactory.setConnectionFactory(mqQueueConnectionFactory()); defaultJmsListenerContainerFactory.setPubSubDomain(true); configurer.configure(defaultJmsListenerContainerFactory,mqQueueConnectionFactory()); defaultJmsListenerContainerFactory.setClientId("producer"); return defaultJmsListenerContainerFactory; } @Bean public JmsTemplate jmsTemplate(){ JmsTemplate jmsTemplate=new JmsTemplate(); jmsTemplate.setPubSubDomain(true); jmsTemplate.setDefaultDestinationName(notificationTopicName); jmsTemplate.setConnectionFactory(mqQueueConnectionFactory()); return jmsTemplate; } }
2. application.yaml配置
jms-mq: notification: conn-name: edb2pre2.momentum.co.za(1414) queueManager: edb2pre1.que.mgr channel: TCP.CLT.CHL
3. 消息发送代码
try { String jsonMessage= objectMapper.writeValueAsString(notification); jmsTemplate.convertAndSend(notificationTopicName,jsonMessage); } catch (JsonProcessingException | JmsException e) { throw new RuntimeException(e); }
错误原因
MQQueueConnectionFactory是IBM MQ针对点对点(PTP)队列域设计的专用连接工厂,它创建的Session是QueueSession,仅支持队列相关操作,无法执行createTopic这类发布订阅域的操作。即便在JmsTemplate和Listener容器中设置了setPubSubDomain(true),底层连接工厂的域限制依然会导致操作无效。
解决方法
将专用的MQQueueConnectionFactory替换为通用的MQConnectionFactory,它同时支持点对点和发布订阅两个域的操作,能够适配Topic的创建与消息发送需求。
修改后的连接工厂创建代码如下:
@Bean public MQConnectionFactory mqConnectionFactory() { MQConnectionFactoryFactory mqConnectionFactoryFactory = new MQConnectionFactoryFactory(mqConfigurationProperties(), null); MQConnectionFactory mqConnectionFactory = mqConnectionFactoryFactory.createConnectionFactory(MQConnectionFactory.class); return mqConnectionFactory; }
同时需要更新配置类中所有引用该Bean的地方(jmsListenerContainerFactory和jmsTemplate的setConnectionFactory方法参数),确保使用新的mqConnectionFactoryBean。
内容的提问来源于stack exchange,提问作者Amby

