如何在 MappingJackson2MessageConverter 中设置 typeIdPropertyName

How to set typeIdPropertyName in MappingJackson2MessageConverter

使用 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
    }
}

还有其他更好的方法吗?

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

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

typeId 可以是 class 名称,也可以是 typeId 映射图中条目的键。

如果您的消息不包含任何类型信息,您需要子class 转换器并覆盖 getJavaTypeForMessage() 到 return 杰克逊 JavaType 作为目标 class,例如:

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

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

这项工作:

@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)  {


}

以及 Java脚本 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 header 添加到要发送的消息中。您需要发送 Java class.

的完整包路径

看起来没有简单的解决方案可以在消息中没有 "type header" 的情况下自动将 @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 converter = new MappingJackson2MessageConverter(); factory.setMessageConverter(converter);

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

  1. @JsonTypeInfo
  2. 定义摘要 class
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
public abstract class AbstractMessage implements Serializable {

}

  1. 定义 POJO 扩展 AbstractMessage
public class Xxx extends AbstractMessage {
    // some properties
}

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