基于Java 1.9与Spring 5的事务化JMS端点注册及配置问询
好的,针对你提出的两个JMS配置需求,我来给你详细说明实现方案:
当然可以给JMS监听器加上事务支持,Spring提供了两种灵活的实现方式:
方式一:配置全局默认事务管理器
先定义一个JmsTransactionManager的Bean,让Spring自动关联到JMS监听器注册器,所有通过该注册器创建的端点都会默认使用事务:
@Configuration public class Endpointer implements JmsListenerConfigurer { @Autowired private ConnectionFactory connectionFactory; // 定义JMS事务管理器Bean @Bean public JmsTransactionManager jmsTransactionManager() { JmsTransactionManager transactionManager = new JmsTransactionManager(); transactionManager.setConnectionFactory(connectionFactory); return transactionManager; } @Override public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { // 给注册器绑定全局事务管理器 registrar.setTransactionManager(jmsTransactionManager()); SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); endpoint.setId("globalTransactionalEndpoint"); endpoint.setDestination("myGlobalQueue"); // 后续配置消息监听器逻辑... registrar.registerEndpoint(endpoint); } }
方式二:给单个端点单独配置事务
如果只想给特定端点启用事务,可以直接给SimpleJmsListenerEndpoint单独指定事务管理器:
@Override public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); endpoint.setId("singleTransactionalEndpoint"); endpoint.setDestination("myTransactionalQueue"); // 仅给当前端点绑定事务管理器 endpoint.setTransactionManager(jmsTransactionManager()); // 后续配置消息监听器逻辑... registrar.registerEndpoint(endpoint); }
配置完成后,监听器处理消息时若抛出异常,事务会自动回滚,消息会重新进入队列(具体重发策略取决于你的JMS Broker配置)。
要实现这个需求,你需要借助MessageListenerAdapter——它能自动完成消息格式转换,同时支持灵活的方法参数(包括JMS头信息)。具体步骤如下:
第一步:配置消息转换器
先定义一个MessageConverter的Bean,这里用Spring自带的SimpleMessageConverter(可直接将TextMessage转为字符串);如果需要处理JSON等复杂格式,可以替换为MappingJackson2MessageConverter:
@Bean public MessageConverter jmsMessageConverter() { SimpleMessageConverter converter = new SimpleMessageConverter(); // 如需处理其他消息类型,可在此扩展配置 return converter; }
第二步:用MessageListenerAdapter包装Lambda监听器
创建JMS端点时,不要直接给setMessageListener传onMessage风格的实现,而是用MessageListenerAdapter适配你的Lambda逻辑,同时绑定消息转换器:
@Override public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); endpoint.setId("converterLambdaEndpoint"); endpoint.setDestination("stringMessageQueue"); // 绑定事务管理器(如果需要事务支持) endpoint.setTransactionManager(jmsTransactionManager()); // 创建消息监听适配器 MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(); listenerAdapter.setMessageConverter(jmsMessageConverter()); // 定义Lambda形式的处理逻辑:接收字符串消息体+指定JMS头 listenerAdapter.setDelegate((String messageBody, @Header("JMSMessageID") String messageId) -> { // 这里编写你的业务逻辑 System.out.println("Received message content: " + messageBody); System.out.println("Corresponding JMS Message ID: " + messageId); // 若需触发事务回滚,直接抛出异常即可 // throw new RuntimeException("Trigger transaction rollback"); }); // 指定适配器调用的方法名(Lambda是匿名的,需显式声明) listenerAdapter.setDefaultListenerMethod("accept"); // 给端点绑定适配器 endpoint.setMessageListener(listenerAdapter); registrar.registerEndpoint(endpoint); }
Lambda的参数可以灵活组合:如果你需要获取所有JMS头,可替换为MessageHeaders headers作为参数;也可以用@Header注解指定任意单个头字段,完全适配你的业务需求。
内容的提问来源于stack exchange,提问作者Matthew Campbell

