Search code examples
javaspringrabbitmqspring-rabbit

Why the Spring needs a @Bean on Converter and ClassMapper if I'm setting them directly on RabbitTemplate?


I would like to understand why I need to mark the getMessageConverter and classMapper methods with @Bean even setting them directly in my custom RabbiteTemplate.

Without the @Bean and passing them to private, the message not contains the TypeId correctly in the message.

So, with the @Bean and public methods, I had the correct header on the message:

headers: __TypeId__:    OrderProducer

Without them, the message contains the incorrect TypeId:

headers: __TypeId__:    com.projet.order.message.OrderProducer

What is the default behavior without the classMapper.

See the code:

@Bean(name = "sendCommandOrderCreate")
public RabbitTemplate sendCommandOrderCreate() {
    RabbitTemplate rabbitTemplate = createRabbitTemplate(getMessageConverter());
    rabbitTemplate.setExchange("order.exchange");
    rabbitTemplate.setRoutingKey("order.cmd.create");
    return rabbitTemplate;
}

private RabbitTemplate createRabbitTemplate(final MessageConverter messageConverter) {
    final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMessageConverter(messageConverter);
    return rabbitTemplate;
}

@Bean
public Jackson2JsonMessageConverter getMessageConverter() {
    Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
    jackson2JsonMessageConverter.setClassMapper(classMapper());
    return jackson2JsonMessageConverter;
}

@Bean
public DefaultClassMapper classMapper() {
    DefaultClassMapper classMapper = new DefaultClassMapper();
    Map<String, Class<?>> idClassMapping = new HashMap<>();
    idClassMapping.put(OrderProducer.class.getSimpleName(), OrderProducer.class);
    idClassMapping.put(OrderConsumer.class.getSimpleName(), OrderConsumer.class);
    classMapper.setIdClassMapping(idClassMapping);
    return classMapper;
}

And how I'm injecting the bean in a Service:

public OrderService(@Qualifier("sendCommandOrderCreate") RabbitTemplate rabbitTemplate) {
    this.rabbitTemplate = rabbitTemplate;
}

Solution

  • I found the problem. I resolved using another Mapper: DefaultJackson2JavaTypeMapper.

    private DefaultJackson2JavaTypeMapper classMapper() {
        DefaultJackson2JavaTypeMapper classMapper = new DefaultJackson2JavaTypeMapper();
        Map<String, Class<?>> idClassMapping = new HashMap<>();
        idClassMapping.put(OrderProducer.class.getSimpleName(), OrderProducer.class);
        idClassMapping.put(OrderConsumer.class.getSimpleName(), OrderConsumer.class);
        classMapper.setIdClassMapping(idClassMapping);
        return classMapper;
    }
    

    I could found this mapper after debugging the code and looking for the different implementations of ClassMapper. The DefaultClassMapper can't provide the behavior that I was expecting and I cant' explain why declare it with @Bean could change his behavior (maybe is a bug on Spring).