Spring jms 侦听器在转换为 Message<Object> 时会覆盖 id 标头

问题描述

我有一个 jms 消息,标题中有一个名为 id 的属性

在我的监听器中,当这个 jms 消息被转换为 Message 时

  @JmsListener(id = "cis-listener",destination = "${amiga.service.jms.cis-listener.destination-fqdn}",containerFactory = "containerFactoryListener")
  public void receiveMessage(Message<Object> event) throws UnkNownMessageException {
  ...

当 MessageHeaders 被实例化时,这个属性会被生成的 UUID 覆盖:

    protected MessageHeaders(@Nullable Map<String,Object> headers,@Nullable UUID id,@Nullable Long timestamp) {
    this.headers = (headers != null ? new HashMap<>(headers) : new HashMap<>());

    if (id == null) {
        this.headers.put(ID,getIdGenerator().generateId());
    }
    ...

是否有一些选项可以重命名我在消息中收到的 id 标头属性以保留该值?

我可以通过使用 javax.jms.Message 更改侦听器接收到的对象来访问此属性,但我更喜欢使用 spring-jms 消息实现。

解决方法

您可以向侦听器适配器添加自定义 JmsHeaderMapper

  • 将容器工厂 autoStartup 设置为 false
  • JmdListenerContainerEndpointRegistry(通过 id)获取容器。
  • 从容器中获取消息侦听器并将其转换为 AbstractAdaptableMessageListener
  • 设置自定义标头映射器。
  • 启动容器。

您的映射器应该将您的外部 id 标头映射到其他内容。 id 在 spring-messaging 中保留。它可以是 SimpleJmsHeaderMapper 的子类。

,

您好 Gary Russell 首先感谢您的回复和帮助,很高兴能得到 Spring 部分开发人员的回答,我有疑问。

在您的解决方案中,我不喜欢被迫禁用自动启动。在我们的项目中,我们不强制自动启动,但如果我们需要此属性将被忽略,因为我们在设置自定义标头映射器后强制启动。我们应该在容器启动之前定义其他属性并检查其值。更改容器自动启动属性的含义有点奇怪。

我也对 MessageHeader 定义有疑问。我将 Message 和 MessageHeader 类视为 jms Message 类的包装器/装饰器。我知道定义这两个类是为了使实例化和访问消息和标头属性更容易。 这就是为什么我不明白为什么时间戳和 id 值被覆盖的原因。我认为包装器不应该添加这种逻辑,尤其是在消息标题中接收 id 属性并不罕见时。如果 spring 需要一个 id 来处理消息,那么在创建之前,应该检查属性 id 是否存在并使用其他名称代替 id,与时间戳相同。 这两个属性名称由 spring 保留,但 jms 定义不允许使用。

我不喜欢该解决方案,但我认为我们将被迫更改我们的侦听器以接收 jms 消息,尽管实例化消息来测试它更困难,但我认为这是一个更好的选择。

再次感谢您的时间和帮助。