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

@JmsListener消息校验无效时如何阻止消息回滚?

解决JMS消息校验异常时阻止回滚的方案

当使用LocalValidatorFactoryBean和javax.validation注解在@JmsListener中校验消息载荷时,校验失败抛出的MethodArgumentNotValidException会触发容器自动回滚事务。由于ErrorHandler是回滚后才执行,无法阻止回滚,我们可以通过以下两种方案实现需求:

方案一:自定义消息处理方法工厂,捕获校验异常并提交事务

通过扩展DefaultMessageHandlerMethodFactory,在校验异常发生时手动获取Session并提交事务,绕过容器的自动回滚逻辑:

@EnableJms
@Configuration
public class ActiveMQConfig implements JmsListenerConfigurer {

    private static final Logger log = LoggerFactory.getLogger(ActiveMQConfig.class);

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(customMethodFactory());
    }

    @Bean
    public DefaultMessageHandlerMethodFactory customMethodFactory() {
        return new DefaultMessageHandlerMethodFactory() {
            @Override
            protected InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
                InvocableHandlerMethod originalHandler = super.createInvocableHandlerMethod(bean, method);
                return new InvocableHandlerMethod(bean, method) {
                    @Override
                    public Object invoke(Object... arguments) throws Exception {
                        try {
                            return originalHandler.invoke(arguments);
                        } catch (ListenerExecutionFailedException e) {
                            // 仅处理校验异常
                            if (e.getCause() instanceof MethodArgumentNotValidException) {
                                // 遍历参数找到Session并提交
                                for (Object arg : arguments) {
                                    if (arg instanceof Session) {
                                        ((Session) arg).commit();
                                        log.warn("消息校验失败,已提交事务阻止回滚: {}", e.getMessage());
                                        return null;
                                    }
                                }
                            }
                            // 其他异常正常抛出,交由原有逻辑处理
                            throw e;
                        }
                    }
                };
            }
        };
    }

    @Bean
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }
}

方案二:自定义监听容器,拦截校验异常并控制事务提交

修改自定义的AdtMessageListenerContainer,重写invokeListener方法,在校验异常发生时直接提交事务:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.listener.adapter.ListenerExecutionFailedException;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

public class AdtMessageListenerContainer extends DefaultMessageListenerContainer {

    private static final Logger log = LoggerFactory.getLogger(AdtMessageListenerContainer.class);

    @Override
    protected void invokeListener(Session session, Message message) throws JMSException {
        try {
            super.invokeListener(session, message);
        } catch (ListenerExecutionFailedException e) {
            // 判断是否为校验异常
            if (e.getCause() instanceof MethodArgumentNotValidException) {
                session.commit();
                log.warn("消息校验无效,已提交事务避免回滚: {}", e.getMessage());
            } else {
                // 非校验异常按原有逻辑处理(触发回滚)
                throw e;
            }
        }
    }
}

注意事项

  1. 两种方案都仅针对校验异常(MethodArgumentNotValidException)执行提交操作,业务异常(如你的SpecificException)仍会按原有逻辑回滚。
  2. 由于容器配置了setSessionTransacted(true),手动提交会覆盖容器的自动事务管理逻辑,需确保日志记录完整,便于后续排查无效消息。
  3. 建议将校验失败的消息转发到死信队列,避免消息丢失或重复消费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:29:59