Spring boot _消息中存在AMQ_组_ID,但@JmsListener中存在JMSXGroupID 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

根据本文件:

消息组中的消息共享相同的组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,  ...