Spring Boot 3.3.0中Session.CLIENT_ACKNOWLEDGE手动消息确认失效求助
Spring Boot 3.3.0 JMS手动确认失效问题解决方案
问题背景
在Spring Boot 3.3.0 + Java 21环境下,配置SimpleJmsListenerContainerFactory并设置Session.CLIENT_ACKNOWLEDGE后,JMS监听器仍自动确认消息(注释message.acknowledge()后消息仍被ActiveMQ移除),但手动编写的原生JMS API代码能正常实现手动确认逻辑。
核心原因
Spring JMS的@JmsListener普通方法在CLIENT_ACKNOWLEDGE模式下,默认会在方法成功返回后自动调用消息确认,无论开发者是否手动执行message.acknowledge()。只有通过特定配置或接口实现,才能完全手动控制消息确认时机。
解决方案
方案1:实现SessionAwareMessageListener接口
实现该接口后,Spring不会自动触发消息确认,需开发者手动调用message.acknowledge()完成确认:
- 编写监听器类
@Component public class TestQueueListener implements SessionAwareMessageListener<Message> { private static final Logger log = LoggerFactory.getLogger(TestQueueListener.class); @Override public void onMessage(Message message, Session session) throws JMSException { try { TextMessage textMessage = (TextMessage) message; log.info("Received message: {}", textMessage.getText()); // 业务处理完成后手动确认,注释此代码则消息不会被移除 // message.acknowledge(); } catch (Exception e) { log.error("Message processing failed", e); // 异常时恢复会话,触发消息重新投递 session.recover(); } } }
- 配置监听器容器
@Bean public DefaultMessageListenerContainer testQueueContainer(ConnectionFactory connectionFactory, TestQueueListener listener) { DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setDestinationName("test.queue"); container.setMessageListener(listener); container.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); container.setSessionTransacted(false); return container; }
方案2:配置容器禁用自动确认
使用DefaultJmsListenerContainerFactory替代SimpleJmsListenerContainerFactory,关闭会话关闭时的自动确认逻辑:
- 修改容器工厂配置
@Bean JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); factory.setSessionTransacted(false); // 禁用会话关闭时的自动确认,完全由手动调用控制 factory.setAcknowledgeOnSessionClose(false); return factory; }
- 保持原
@JmsListener方法不变
@JmsListener(destination = "test.queue", containerFactory = "jmsListenerContainerFactory") public void configure(Message message) { try { TextMessage textMessage = (TextMessage) message; log.info("Received message {} ", textMessage.getText()); // 仅当调用此方法时,消息才会被确认移除 // message.acknowledge(); } catch (Exception e) { log.error("Message processing failed.", e); } }
额外检查项
- 确保
application.properties/yml中未覆盖ack模式配置:# 避免Spring Boot自动配置覆盖手动设置 spring.jms.acknowledge-mode=client - 检查项目中是否存在其他同名的
JmsListenerContainerFactoryBean,避免容器注入错误。
内容的提问来源于stack exchange,提问作者Bharad
相关产品推荐
相关产品推荐

