如何在 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 中开始新的活动?