原因:org.springframework.jms.support.converter.MessageConversionException:无法在消息中找到类型 ID 属性 [_type]
Caused by: org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message
我正在尝试这个 spring JMS 示例,但出现错误。
https://spring.io/guides/gs/messaging-jms/
原因:org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message from destination [queue://mailbox]
有趣的是,如果我克隆它并且 运行 一切 运行 都很好。如果我复制和粘贴,它会出错。
@Bean // Serialize message content to json using TextMessage
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
这段代码实际上导致了错误。搜索网络和文档,我仍然不知道如何以及如何设置 setTypeIdPropertyName 值以及“_type”在这个项目中指的是什么?由于消息没有这样的属性,那么它来自哪里?
TypeIdPropertyName
是标识实体的 属性 的名称。 Jackson 映射器应该知道在反序列化传入的 JSON.
时使用什么实体
请求应如下所示:
{
"_type" : "hello.Email",
"to" : "Imran",
"from" : "dzatorsky"
}
顺便说一句,我认为这不是最佳解决方案,因为 JMS 已经知道要使用什么类型(您在方法中声明了它)。另一个缺点是您在难以维护的消息中指定实体和包的名称(包或实体名称的每次更改都会很痛苦)。
这里是更强大的配置:
@EnableJms
@Configuration
public class JmsListenerConfig implements JmsListenerConfigurer {
@Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(messageConverter());
return factory;
}
@Bean
public MessageConverter messageConverter() {
return new MappingJackson2MessageConverter();
}
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}
}
此外,如果您使用 Spring JmsTemplate 发送消息,您可以将此组件添加到您的配置中:
/**
* Used to convert JMS messages from/to JSON. Registered in Spring-JMS automatically via auto configuration
*/
@Component
public class JsonMessageConverter implements MessageConverter {
@Autowired
private ObjectMapper mapper;
/**
* Converts message to JSON. Used mostly by {@link org.springframework.jms.core.JmsTemplate}
*/
@Override
public javax.jms.Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
String json;
try {
json = mapper.writeValueAsString(object);
} catch (Exception e) {
throw new MessageConversionException("Message cannot be parsed. ", e);
}
TextMessage message = session.createTextMessage();
message.setText(json);
return message;
}
/**
* Extracts JSON payload for further processing by JacksonMapper.
*/
@Override
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
return ((TextMessage) message).getText();
}
}
使用此配置,您可以跳过邮件中烦人的“_type”字段。
自定义(即应用程序级)“_type”属性 必须是在消息上设置的 JMS 属性(由其生产者)。消息有效负载中没有乱七八糟的类型元数据。要了解 JMS 消息属性,应访问 https://docs.oracle.com/javaee/7/api/javax/jms/Message.html
这 不是 与 JSON 属性 混淆,后者可以替代使用并且必须使用基于 Jackson 的注释进行配置(例如多态反序列化)。在这种情况下,实际消息有效负载(JSON 字符串)已更改并在顶级对象中包含“_type”属性。
其他答案没有指定在调用方设置类型,所以我会指出这一点。您需要在调用方和接收方都使用消息转换器(假设您不只是在玩一个应用程序):
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
Spring 将自动将此 messageConverter 与 JmsTemplate 一起使用(如果您正在使用它)。而“_type”可以是任何东西,但两边应该是一样的。
我正在使用 Spring Boot JmsTemplate 和 Danylo Zatorsky 的第二个 class 答案对我来说不太适用,因为它的反序列化仅 returns 简单字符串。在序列化期间在内容前加上 class 名称,稍后使用正则表达式将其破解,这样就可以反转更复杂的对象。 HTH
@Component
public class JsonMessageConverter implements MessageConverter {
private final ObjectMapper mapper;
public JsonMessageConverter(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public javax.jms.Message toMessage(Object object, Session session) throws MessageConversionException {
try {
// send class=<json content>
return session.createTextMessage(object.getClass().getName() + "=" + mapper.writeValueAsString(object));
} catch (Exception e) {
throw new MessageConversionException("Message cannot be serialized", e);
}
}
@Override
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
try {
Matcher matcher = Pattern.compile("^([^=]+)=(.+)$").matcher(((TextMessage) message).getText());
if (!matcher.find())
{
throw new MessageConversionException("Message is not of the expected format: class=<json content>");
}
return mapper.readValue(matcher.group(2), Class.forName(matcher.group(1)));
} catch (Exception e) {
throw new MessageConversionException("Message cannot be deserialized", e);
}
}
}
我正在尝试这个 spring JMS 示例,但出现错误。
https://spring.io/guides/gs/messaging-jms/
原因:org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message from destination [queue://mailbox]
有趣的是,如果我克隆它并且 运行 一切 运行 都很好。如果我复制和粘贴,它会出错。
@Bean // Serialize message content to json using TextMessage
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
这段代码实际上导致了错误。搜索网络和文档,我仍然不知道如何以及如何设置 setTypeIdPropertyName 值以及“_type”在这个项目中指的是什么?由于消息没有这样的属性,那么它来自哪里?
TypeIdPropertyName
是标识实体的 属性 的名称。 Jackson 映射器应该知道在反序列化传入的 JSON.
请求应如下所示:
{
"_type" : "hello.Email",
"to" : "Imran",
"from" : "dzatorsky"
}
顺便说一句,我认为这不是最佳解决方案,因为 JMS 已经知道要使用什么类型(您在方法中声明了它)。另一个缺点是您在难以维护的消息中指定实体和包的名称(包或实体名称的每次更改都会很痛苦)。
这里是更强大的配置:
@EnableJms
@Configuration
public class JmsListenerConfig implements JmsListenerConfigurer {
@Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(messageConverter());
return factory;
}
@Bean
public MessageConverter messageConverter() {
return new MappingJackson2MessageConverter();
}
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}
}
此外,如果您使用 Spring JmsTemplate 发送消息,您可以将此组件添加到您的配置中:
/**
* Used to convert JMS messages from/to JSON. Registered in Spring-JMS automatically via auto configuration
*/
@Component
public class JsonMessageConverter implements MessageConverter {
@Autowired
private ObjectMapper mapper;
/**
* Converts message to JSON. Used mostly by {@link org.springframework.jms.core.JmsTemplate}
*/
@Override
public javax.jms.Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
String json;
try {
json = mapper.writeValueAsString(object);
} catch (Exception e) {
throw new MessageConversionException("Message cannot be parsed. ", e);
}
TextMessage message = session.createTextMessage();
message.setText(json);
return message;
}
/**
* Extracts JSON payload for further processing by JacksonMapper.
*/
@Override
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
return ((TextMessage) message).getText();
}
}
使用此配置,您可以跳过邮件中烦人的“_type”字段。
自定义(即应用程序级)“_type”属性 必须是在消息上设置的 JMS 属性(由其生产者)。消息有效负载中没有乱七八糟的类型元数据。要了解 JMS 消息属性,应访问 https://docs.oracle.com/javaee/7/api/javax/jms/Message.html
这 不是 与 JSON 属性 混淆,后者可以替代使用并且必须使用基于 Jackson 的注释进行配置(例如多态反序列化)。在这种情况下,实际消息有效负载(JSON 字符串)已更改并在顶级对象中包含“_type”属性。
其他答案没有指定在调用方设置类型,所以我会指出这一点。您需要在调用方和接收方都使用消息转换器(假设您不只是在玩一个应用程序):
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
Spring 将自动将此 messageConverter 与 JmsTemplate 一起使用(如果您正在使用它)。而“_type”可以是任何东西,但两边应该是一样的。
我正在使用 Spring Boot JmsTemplate 和 Danylo Zatorsky 的第二个 class 答案对我来说不太适用,因为它的反序列化仅 returns 简单字符串。在序列化期间在内容前加上 class 名称,稍后使用正则表达式将其破解,这样就可以反转更复杂的对象。 HTH
@Component
public class JsonMessageConverter implements MessageConverter {
private final ObjectMapper mapper;
public JsonMessageConverter(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public javax.jms.Message toMessage(Object object, Session session) throws MessageConversionException {
try {
// send class=<json content>
return session.createTextMessage(object.getClass().getName() + "=" + mapper.writeValueAsString(object));
} catch (Exception e) {
throw new MessageConversionException("Message cannot be serialized", e);
}
}
@Override
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
try {
Matcher matcher = Pattern.compile("^([^=]+)=(.+)$").matcher(((TextMessage) message).getText());
if (!matcher.find())
{
throw new MessageConversionException("Message is not of the expected format: class=<json content>");
}
return mapper.readValue(matcher.group(2), Class.forName(matcher.group(1)));
} catch (Exception e) {
throw new MessageConversionException("Message cannot be deserialized", e);
}
}
}