Spring boot _消息中存在AMQ_组_ID,但@JmsListener中存在JMSXGroupID null
根据本文件: 消息组中的消息共享相同的组id,即,对于JMS,它们具有相同的组标识符属性JMSXGroupID;对于Apache ActiveMQ Artemis核心API,它们具有相同的组id 当我在代理中浏览值为product=paper的消息时,我可以理解为什么最初通过JMSXGroupID设置的属性会变成_AMQ_GROUP_ID。但是,在我的@JmsListener注释方法中,我可以看到_AMQ_GROUP_ID属性丢失,并且JMSXGroupID在消息头hashmap中显示为null 听众:Spring boot _消息中存在AMQ_组_ID,但@JmsListener中存在JMSXGroupID null,spring-boot,jms,spring-jms,activemq-artemis,Spring Boot,Jms,Spring Jms,Activemq Artemis,根据本文件: 消息组中的消息共享相同的组id,即,对于JMS,它们具有相同的组标识符属性JMSXGroupID;对于Apache ActiveMQ Artemis核心API,它们具有相同的组id 当我在代理中浏览值为product=paper的消息时,我可以理解为什么最初通过JMSXGroupID设置的属性会变成_AMQ_GROUP_ID。但是,在我的@JmsListener注释方法中,我可以看到_AMQ_GROUP_ID属性丢失,并且JMSXGroupID在消息头hashmap中显示为null
@Component
public class CustomSpringJmsListener {
protected final Logger LOG = LoggerFactory.getLogger(getClass());
@JmsListener(destination = "local-queue", subscription = "groupid-example",
containerFactory = "myContainerFactory", concurrency = "15-15")
public void receive(Message message) throws JMSException {
LOG.info("Received message: " + message);
}
}
申请代码:
@SpringBootApplication
@EnableJms
public class GroupidApplication implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(GroupidApplication.class);
@Autowired
private JmsTemplate jmsTemplate;
@Autowired MessageConverter messageConverter;
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(GroupidApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
@Override
public void run(String... args) {
LOG.info("EXECUTING : command line runner");
jmsTemplate.setPubSubDomain(true);
createAndSendObjectMessage("Message1");
createAndSendTextMessage("Message2");
createAndSendTextMessage("Message3");
createAndSendTextMessage("Message4");
createAndSendTextMessage("Message5");
createAndSendTextMessage("Message6");
}
private void createAndSendTextMessage(String messageBody) {
jmsTemplate.send("local-queue", session -> {
Message message = session.createTextMessage(messageBody);
message.setStringProperty("JMSXGroupID", "product=paper");
return message;
});
}
// BEANS
@Bean
public JmsListenerContainerFactory<?> myContainerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
// You could still override some of Boot's default if necessary.
factory.setSubscriptionDurable(true);
factory.setSubscriptionShared(true);
factory.setMessageConverter(messagingMessageConverter());
return factory;
}
@Bean
public MessagingMessageConverter messagingMessageConverter() {
return new MessagingMessageConverter(messageConverter, new GroupIdMessageMapper());
}
}
调用SimpleJMHeaderMapper的位置的堆栈跟踪:
toHeaders:130,SimpleJmsHeaderMapper org.springframework.jms.support
toHeaders:57,SimpleJmsHeaderMapper org.springframework.jms.support
extractHeaders:148,MessagingMessageConverter
org.springframework.jms.support.converter访问$100:466,
AbstractAdaptiableMessageListener$MessagingMessageConverterAdapter
org.springframework.jms.listener.adapter getHeaders:552,
AbstractAdaptiableMessageListener$MessagingMessageConverterAdapter$LazyResolutionMessage
org.springframework.jms.listener.adapter内部:68,
人头模型
org.springframework.messaging.handler.annotation.support
resolveArgument:100,AbstractNamedValueMethodArgumentResolver
org.springframework.messaging.handler.annotation.support
resolveArgument:117,HandlerMethodArgumentResolverComposite
org.springframework.messaging.handler.invocation
getMethodArgumentValues:148,InvocableHandlerMethod
org.springframework.messaging.handler.invocation调用:116,
发票处理方法
org.springframework.messaging.handler.invocation invokeHandler:114,
MessagingMessageListenerAdapter
org.springframework.jms.listener.adapter-onMessage:77,
MessagingMessageListenerAdapter
org.springframework.jms.listener.adapter doInvokeListener:736,
AbstractMessageListenerContainer org.springframework.jms.listener
invokeListener:696,AbstractMessageListenerContainer
org.springframework.jms.listener doExecuteListener:674,
AbstractMessageListenerContainer org.springframework.jms.listener
doReceiveAndExecute:318,AbstractPollingMessageListenerContainer
org.springframework.jms.listener接收和执行:257,
AbstractPollingMessageListenerContainer
org.springframework.jms.listener invokeListener:1190,
DefaultMessageListenerContainer$AsyncMessageListenerInvoker
org.springframework.jms.listener ExecuteongLoop:1180,
DefaultMessageListenerContainer$AsyncMessageListenerInvoker
org.springframework.jms.listener运行:1077,
DefaultMessageListenerContainer$AsyncMessageListenerInvoker
org.springframework.jms.listener运行:748,线程java.lang
尝试子类化SimpleJMHeaderMapper并覆盖toHeaders。调用super.toHeaders,根据结果创建一个新映射;将所需的任何其他标题放入映射中,并从映射中返回新的MessageHeaders
将自定义映射器传递到新的MessagingMessageConverter,并将其传递到容器工厂
如果您使用的是SpringBoot,只需将转换器添加为@Bean,Boot就会自动将其连接到工厂中
编辑
毕竟,;我刚刚写了一个应用程序,它对我来说没有任何定制,一切都很好
@SpringBoot应用程序
公共类SO58399905应用程序{
公共静态无效字符串[]args{
SpringApplication.runSo58399905Application.class,args;
}
@JmsListenerdestination=foo
public void listenString in,MessageHeaders{
System.out.PrintLIn+标题;
}
@豆子
公共应用程序运行程序Runner JmsTemplate模板{
return args->template.convertAndSendfoo,bar,msg->{
msg.setStringPropertyJMSXGroupID,product=x;
返回味精;
};
}
}
及
编辑2
这是artemis客户端中的一个bug—在2.6.4 Boot 2.1.9中,只有getStringProperty在获取JMSXGroupID时返回_AMQ_GROUP_ID属性的值
映射程序使用返回null的getObjectProperty。与第2.10.1条客户进行沟通;消息正确地从getObjectProperty返回了_AMQ_GROUP_ID属性的值。很抱歉,您是否建议我自己在侦听器端静态地将标题放入映射中?我需要它们来自队列中的消息头。我误解了吗?没有;我假设Artemis正在发送它,这只是一个猜测,只是默认的Spring头映射器对它一无所知,所以它没有映射到用于调用JmsListener的消息中;我刚刚查看了消息javadocs:{@code JMSXGroupID}和{@code JMSXGroupSeq}是客户机在对消息进行分组时应该使用的标准属性。我假设它是一个第一类的头,而不是一个简单的属性。默认映射器映射所有属性,因此如果是nu
那么,这意味着Artemis客户端没有在Jms消息中提供它。好吧,要清楚,将自定义头名称映射到标准头名称不是Spring的责任。它当然不会在出站端做相反的事情;映射程序不知道JMS提供程序。阿耳特弥斯一定是在做这件事;它们应该是一致的,并且在入站一侧对称地映射;我在boot2.1.9中也得到了null。这是artemis客户端中的一个bug—在2.6.4版本中,只有getStringProperty返回_AMQ_GROUP_ID属性的值。映射程序使用返回null的getObjectProperty。与第2.10.1条客户进行沟通;消息正确地从getObjectProperty返回_AMQ_GROUP_ID属性的值。
@Component
public class CustomSpringJmsListener {
protected final Logger LOG = LoggerFactory.getLogger(getClass());
@JmsListener(destination = "local-queue", subscription = "groupid-example",
containerFactory = "myContainerFactory", concurrency = "15-15")
public void receive(Message message) throws JMSException {
LOG.info("Received message: " + message);
}
}
@SpringBootApplication
@EnableJms
public class GroupidApplication implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(GroupidApplication.class);
@Autowired
private JmsTemplate jmsTemplate;
@Autowired MessageConverter messageConverter;
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(GroupidApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
@Override
public void run(String... args) {
LOG.info("EXECUTING : command line runner");
jmsTemplate.setPubSubDomain(true);
createAndSendObjectMessage("Message1");
createAndSendTextMessage("Message2");
createAndSendTextMessage("Message3");
createAndSendTextMessage("Message4");
createAndSendTextMessage("Message5");
createAndSendTextMessage("Message6");
}
private void createAndSendTextMessage(String messageBody) {
jmsTemplate.send("local-queue", session -> {
Message message = session.createTextMessage(messageBody);
message.setStringProperty("JMSXGroupID", "product=paper");
return message;
});
}
// BEANS
@Bean
public JmsListenerContainerFactory<?> myContainerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
// You could still override some of Boot's default if necessary.
factory.setSubscriptionDurable(true);
factory.setSubscriptionShared(true);
factory.setMessageConverter(messagingMessageConverter());
return factory;
}
@Bean
public MessagingMessageConverter messagingMessageConverter() {
return new MessagingMessageConverter(messageConverter, new GroupIdMessageMapper());
}
}
bar{jms_redelivered=false, JMSXGroupID=product=x, jms_deliveryMode=2, JMSXDeliveryCount=1, ...