Вызвано: org.springframework.jms.support.converter.MessageConversionException: Не удалось найти свойство id типа [_type] в сообщении

Я пытаюсь этой весной образец 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", на что оно ссылается в этом проекте? Поскольку сообщение не имеет такого свойства, то откуда оно берется?

5 ответов

Решение

TypeIdPropertyName это имя свойства, которое идентифицирует сущность. Картограф Джексона должен знать, какую сущность использовать при десериализации входящего 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" в ваших сообщениях.

Другие ответы не указывали установку типа на вызывающей стороне, поэтому я укажу на это. Вам нужен конвертер сообщений на ОБА вызывающей и принимающей стороне (при условии, что вы не просто играете с одним приложением):

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

Spring автоматически использует этот messageConverter с JmsTemplate (если это то, что вы используете). И "_type" может быть чем угодно, но он должен быть одинаковым с обеих сторон.

Настраиваемое (т. Е. Прикладное) свойство "_type" должно быть свойством JMS, установленным в сообщении (его производителем). Полезная нагрузка сообщения не завалена метаданными типа. Чтобы узнать о свойствах сообщений JMS, посетите веб- сайт https://docs.oracle.com/javaee/7/api/javax/jms/Message.html

Это не следует путать со свойством JSON, которое может использоваться альтернативно и должно быть сконфигурировано с помощью аннотаций на основе Джексона (например, как полиморфная десериализация). В этом случае фактическая полезная нагрузка сообщения (строка JSON) изменяется и содержит свойство _type в объекте верхнего уровня.

В моем случае я былsetTypeIdProprtyNameнеправильный.

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

Я использую Spring Boot JmsTemplate, и второй класс ответа Данила Заторского не совсем сработал для меня, поскольку его десериализация возвращает только простые строки. Предварительное добавление к содержимому имени класса во время сериализации и его последующее взламывание с помощью регулярного выражения позволяет реверсировать более сложные объекты. ХТН

      @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);
        }
    }
}
Другие вопросы по тегам