Rabbitmq template message transmitted, Date type field eight hours later than the current time

Foreword

Before the burst of the development process problems encountered with rabbitmq templatesending a message, the message body in time is 8 hours less than the current time, this is a look at the problem area.

Why is there talk about it.

Before the configuration is as follows:

@Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);

        template.setMessageConverter(new Jackson2JsonMessageConverter());
        template.setMandatory(true);
      
        ...
        return template;
    }

To vo message sent out is this:

@Data
public class TestVO {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date testDate;
}

Then, the problem is that the message body, the less time than the current eight hours time.

{"testDate":"2019-12-27 05:45:26"}

the reason

We are so use rabbitmq template is:

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisRepository redisRepository;

    /**
     * 发送消息
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param msgMbject 消息体,无需序列化,会自动序列化为json
     */
    public void send(String exchange, String routingKey, final Object msgMbject) {
        CorrelationData correlationData = new CorrelationData(GUID.generate());
        CachedMqMessageForConfirm cachedMqMessageForConfirm = new CachedMqMessageForConfirm(exchange, routingKey, msgMbject);
        redisRepository.saveCacheMessageForConfirms(correlationData,cachedMqMessageForConfirm);
        //核心代码:这里,发送出去的msgObject其实就是一个vo或者dto,rabbitmqTemplate会自动帮我们转为json
        rabbitTemplate.convertAndSend(exchange,routingKey,msgMbject,correlationData);
    }

I explained in comments, rabbitmq will automatically do the conversion, the conversion is jackson.

Source can follow up to find out:

org.springframework.amqp.rabbit.core.RabbitTemplate#convertAndSend
  
    @Override
    public void convertAndSend(String exchange, String routingKey, final Object object,
            @Nullable CorrelationData correlationData) throws AmqpException {
        // 这里调用了convertMessageIfNecessary(object)
        send(exchange, routingKey, convertMessageIfNecessary(object), correlationData);
    }

Call convertMessageIfNessary:

protected Message convertMessageIfNecessary(final Object object) {
        if (object instanceof Message) {
            return (Message) object;
        }
        // 获取消息转换器
        return getRequiredMessageConverter().toMessage(object, new MessageProperties());
    }

Get message code converter is as follows:


    private MessageConverter getRequiredMessageConverter() throws IllegalStateException {
        MessageConverter converter = getMessageConverter();
        if (converter == null) {
            throw new AmqpIllegalStateException(
                    "No 'messageConverter' specified. Check configuration of RabbitTemplate.");
        }
        return converter;
    }

getMessageConverter is to get a field rabbitmqTemplate class.

    public MessageConverter getMessageConverter() {
        return this.messageConverter;
    }

We just see where it can carry out the assignment.

Then I think of it, that is our business code assignment:

@Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        // 下面这里赋值了。。。差点搞忘了
        template.setMessageConverter(new Jackson2JsonMessageConverter());
        template.setMandatory(true);

        return template;
    }

Anyway, it, in general, is rabbitmqTemplate use messageConverter convert our custom message before sending.

Time zone problem, good reproducibility, source code:

https://gitee.com/ckl111/all-simple-demo-in-work/tree/master/jackson-demo

@Data
public class TestVO {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date testDate;
}

Test code:


    @org.junit.Test
    public void normal() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        TestVO vo = new TestVO();
        vo.setTestDate(new Date());
        String value = mapper.writeValueAsString(vo);
        System.out.println(value);
    }

Output:

{"testDate":"2019-12-27 05:45:26"}

Solution

  1. When specify a default zone configuration

     @org.junit.Test
        public void specifyDefaultTimezone() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            SerializationConfig oldSerializationConfig = mapper.getSerializationConfig();
            /**
             * 新的序列化配置,要配置时区
             */
            String timeZone = "GMT+8";
            SerializationConfig newSerializationConfig = oldSerializationConfig.with(TimeZone.getTimeZone(timeZone));
    
            mapper.setConfig(newSerializationConfig);
            TestVO vo = new TestVO();
            vo.setTestDate(new Date());
            String value = mapper.writeValueAsString(vo);
            System.out.println(value);
        }
  2. On the field annotated

    @Data
    public class TestVoWithTimeZone {
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date testDate;
    }

    We here, added timezone, manually specify the time zone configuration.

    Test code:

    
        @org.junit.Test
        public void specifyTimezoneOnField() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            TestVoWithTimeZone vo = new TestVoWithTimeZone();
            vo.setTestDate(new Date());
            String value = mapper.writeValueAsString(vo);
            System.out.println(value);
        }

Output above two are correct.

There is no source code to analyze, simply look at the sequence of the time, there will be a sequence of configuration; this configuration consists of two parts: the default configuration + class custom configuration. Custom configuration will override the default configuration.

Our second way is to modify the default configuration; third way is to use custom configuration override the default configuration.

jacksonQuite important, especially spring cloudfamily bucket, feignalso with this, restTemplatealso used, as well as Spring MVCin the httpmessageConverterinterest of the students, look at this place below it.

If you are interested JsonFormat processing, you can see the following places:

com.fasterxml.jackson.annotation.JsonFormat.Value # Value (com.fasterxml.jackson.annotation.JsonFormat) (make a break here, and then run a test on here)

to sum up

I almost forgot about the problem rabbitmq template, and ultimately our solution is:

@Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);

        ObjectMapper mapper = new ObjectMapper();
        SerializationConfig oldSerializationConfig = mapper.getSerializationConfig();
        /**
         * 新的序列化配置,要配置时区
         */
        String timeZone = environment.getProperty(CadModuleConstants.SPRING_JACKSON_TIME_ZONE);
        SerializationConfig newSerializationConfig = oldSerializationConfig.with(TimeZone.getTimeZone(timeZone));

        mapper.setConfig(newSerializationConfig);

        Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(mapper);

        template.setMessageConverter(messageConverter);
        template.setMandatory(true);
      
        ...设置callback啥的
        return template;
    }

More relevant source code:

https://gitee.com/ckl111/all-simple-demo-in-work/tree/master/jackson-demo

Guess you like

Origin www.cnblogs.com/grey-wolf/p/12107016.html