如何在 MappingJackson2MessageConverter 中设置 typeIdPropertyName

Posted

技术标签:

【中文标题】如何在 MappingJackson2MessageConverter 中设置 typeIdPropertyName【英文标题】:How to set typeIdPropertyName in MappingJackson2MessageConverter 【发布时间】:2016-07-26 15:25:55 【问题描述】:

对于Spring4 + ActiveMQ,我想从队列接收 JMS 消息并自动转换为 POJO。我将MappingJackson2MessageConverter 添加到DefaultJmsListenerContainerFactory

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() 
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();

    // some other config

    MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
    converter.setTargetType(MessageType.TEXT);
    converter.setTypeIdPropertyName("???");
    factory.setMessageConverter(converter);

    return factory;

这是我的监听器配置

@JmsListener(destination = "queue.fas.flight.order", containerFactory = "jmsListenerContainerFactory")
public void processOrder(OrderRegisterDto registerParam) 
    System.out.println(registerParam.toString());

我的问题是,如何设置TypeIdPropertyName?队列不在我的控制之下;其他人发送JSON给它。

我想要一个通用转换器,所以我使用String 接收消息并手动将其转换为 POJO。

@JmsListener(destination = "xxxx", containerFactory = "xxxxx")
 public void order(String registerParam) 
    try
        OrderRegisterDto dto = objectMapper.readValue(registerParam,OrderRegisterDto.class);
    catch (IOException e)
        // TODO
    

还有其他更好的方法吗?

【问题讨论】:

【参考方案1】:

转换器希望发送者在消息属性中提供转换的类型信息。

String typeId = message.getStringProperty(this.typeIdPropertyName);

typeId 可以是类名,也可以是 typeId 映射映射中条目的键。

如果您的消息不包含任何类型信息,则需要将转换器子类化并覆盖 getJavaTypeForMessage() 以返回目标类的 Jackson JavaType,例如:

return TypeFactory.defaultInstance().constructType(Foo.class);

如果它是一个常量并且不依赖于消息中的某些信息,您可以在您的子类中创建一个静态字段。

【讨论】:

首先,是的,它有效。像这样的子类:@Override protected JavaType getJavaTypeForMessage(Message message) throws JMSException return TypeFactory.defaultInstance().constructType(OrderRegisterDto.class); 但我想要一个通用转换器。用这种方法,只能转换OrderRegisterDto.class,不然呢,再加一个ListenerFactory?所以我使用String接收消息,并手动将其转换为POJO。 OrderRegisterDto dto = objectMapper.readValue(registerParam,OrderRegisterDto.class);还有更好的方法吗? 不要尝试将代码放入 cmets,而是编辑问题。您可以根据需要使转换器子类变得复杂 - 但您需要在消息中提供一些提示来告诉您要创建什么类。 是否有可能不是从消息本身而是从@JmsListener注解方法的目标类型来确定类型?例如,与控制器中发生的标准反序列化相比,这实际上是预期的行为。 我们最近向Spring AMQP 添加了逻辑来做到这一点(从@RabbitListener 推断类型)方法,但它不适用于@JmsListener;您需要手动配置转换器。【参考方案2】:

这项工作:

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() 

    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();

    factory.setConnectionFactory(connectionFactory);


    factory.setConcurrency("3-10");


    // Este es el convertidor por defeto de Spring.
    SimpleMessageConverter s = new SimpleMessageConverter();

    MappingJackson2MessageConverter s2 = new MappingJackson2MessageConverter();
    s2.setTargetType(MessageType.BYTES);
    s2.setTypeIdPropertyName("DocumentType");

    factory.setMessageConverter(s2);  // or "s"

    return factory;

这是监听器:

@JmsListener(containerFactory = "jmsListenerContainerFactory", destination = ORDER_QUEUE)
@Payload final ) 
public void receiveMessage(Session ses, @Payload final Message<Product> message, @Headers final Map<String, Object> headers)  



还有 javascript STOMP 客户端:

 var product = 
              productId : "111",
              name : "laptop",
              quantity: 2
            
 var beforeSend = JSON.stringify(product);
 stompClient.send(destinationProductProd_02,"DocumentType":"org.jms.model.Product", beforeSend);  // OK

请注意,我将“DocumentType”STOMP 标头添加到要发送的消息中。您需要发送 Java 类的完整包路径。

【讨论】:

thx,如果queue 在我的控制之下,那就没问题了。但实际上,其他人将消息发送到queue,不可能添加诸如“DocumentType”之类的额外参数。【参考方案3】:

看起来没有简单的解决方案可以在 @JmsListener 中自动将 JSON 转换为 POJO,而消息中没有“类型标头”。但是可以使用 Jackson 的 ObjectMapper 在您的代码中显式执行此操作:

@Autowired
private ObjectMapper objectMapper;

@JmsListener(destination = "...", containerFactory = "...")
public void processOrder(String payload) 
    OrderRegisterDto dto = objectMapper.readValue(payload, OrderRegisterDto.class);
    System.out.println(dto.toString());

不要忘记在您的配置中删除 MappingJackson2MessageConverter

MappingJackson2MessageConverter 转换器 = new MappingJackson2MessageConverter(); factory.setMessageConverter(converter);

【讨论】:

【参考方案4】:

Jackson 需要类型信息来将 JSON 转换为 POJO; 使用@JsonTypeInfo可以自动生成JSON中的类类型信息 所以我用这种方式解决了这个问题:

    @JsonTypeInfo定义一个抽象类
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
public abstract class AbstractMessage implements Serializable 



    定义 POJO 扩展 AbstractMessage
public class Xxx extends AbstractMessage 
    // some properties


    配置MessageConverter
@Bean
public MessageConverter messageConverter() 
    MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
    messageConverter.setObjectMapper(objectMapper);
    // same with @JsonTypeInfo( property )
    messageConverter.setTypeIdPropertyName("@class");
    return messageConverter;

【讨论】:

以上是关于如何在 MappingJackson2MessageConverter 中设置 typeIdPropertyName的主要内容,如果未能解决你的问题,请参考以下文章

如何在表单提交后保留文本(如何在提交后不删除自身?)

如何在异步任务中调用意图?或者如何在 onPostExecute 中开始新的活动?

在 Avkit 中如何使用这三行代码,以及如何将音乐静音”

如何在 JDBC 中启动事务?

如何在 Fragment 中调用 OnActivityResult 以及它是如何工作的?

如何使用 Firebase 在 Web 上托管 Flutter?它的效果如何?