7

我正在尝试这个 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”它在这个项目中指的是什么?由于消息没有这样的属性,那么它来自哪里?

4

4 回答 4

21

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”字段。

于 2017-08-05T23:11:03.600 回答
3

其他答案没有指定在调用方设置类型,所以我会指出这一点。您需要在调用方和接收方都有一个消息转换器(假设您不只是在玩单个应用程序):

    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }

Spring 将自动将此 messageConverter 与 JmsTemplate 一起使用(如果您正在使用)。“_type”可以是任何东西,但它应该在两边都相同。

于 2018-05-03T18:41:52.990 回答
1

自定义(即应用程序级别)“_type”属性必须是消息(由其生产者)设置的 JMS 属性。消息有效负载没有散布类型元数据。要了解 JMS 消息属性,应该访问https://docs.oracle.com/javaee/7/api/javax/jms/Message.html

不要与 JSON 属性混淆,该属性可以替代使用并且必须使用基于 Jackson 的注释进行配置(例如,作为多态反序列化)。在这种情况下,实际的消息负载(JSON 字符串)已更改,并在顶级对象中包含“_type”属性。

于 2017-09-01T16:13:58.690 回答
0

我正在使用 Spring Boot JmsTemplate,而 Danylo Zatorsky 的第二类答案对我来说并不适用,因为它的反序列化只返回简单的字符串。在序列化过程中在内容前面加上类名,然后用正则表达式破解它,可以反转更复杂的对象。高温高压

@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);
        }
    }
}
于 2020-12-23T14:29:33.220 回答