httpcliet发送body体_rabbitmq template发送的消息中,Date类型字段比当前时间晚8小时...

前言

前一阵开发过程遇到的问题,用的 rabbitmq template 发送消息,消息body里的时间是比当前时间少了8小时的,这种一看就是时区问题了。

就说说为什么出现吧。

之前的配置是这样的:

@Bean    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {        RabbitTemplate template = new RabbitTemplate(connectionFactory);        template.setMessageConverter(new Jackson2JsonMessageConverter());        template.setMandatory(true);              ...        return template;    }

要发送出去的消息vo是这样的:

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

然后,出现的问题就是,消息体里,时间比当前时间少了8个小时。

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

原因

我们是这么使用rabbitmq template的:

@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);    }

注释里我解释了,rabbitmq会自动做转换,转换用的就是jackson。

跟进源码也能一探究竟:

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);    }

调用了convertMessageIfNessary:

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

获取消息转换器的代码如下:

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

getMessageConverter就是获取rabbitmqTemplate 类中的一个field。

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

我们只要看哪里对它进行赋值即可。

然后我想起来,就是在我们业务代码里赋值的:

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

反正呢,总体来说,就是rabbitmqTemplate 会使用我们自定义的messageConverter转换message后再发送。

时区问题,很好重现,源码在:

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

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

测试代码:

@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);    }

输出:

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

解决办法

  1. 指定默认时区配置@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. 在field上加注解@Data public class TestVoWithTimeZone { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date testDate; }我们这里,新增了timezone,手动指定了时区配置。测试代码:@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); }

上面两种的输出都是正确的。

这里没有去分析源码,简单说一下,在序列化的时候,会有一个序列化配置;这个配置由两部分组成:默认配置+这个类自定义的配置。 自定义配置会覆盖默认配置。

我们的第二种方式,就是修改了默认配置;第三种方式,就是使用自定义配置覆盖默认配置。

jackson 还挺重要,尤其是 spring cloud 全家桶, feign 也用了这个, restTemplate 也用了,还有 Spring MVC 里的 httpmessageConverter 有兴趣的同学,去看下面这个地方就可以了。

1541a2f55f34428851c6a21a25eee112.png

如果对JsonFormat的处理感兴趣,可以看下面的地方:

com.fasterxml.jackson.annotation.JsonFormat.Value#Value(com.fasterxml.jackson.annotation.JsonFormat) (打个断点在这里,然后跑个test就到这里了)

546c661bb6eb4849a448885fb7640823.png

总结

差点忘了,针对rabbitmq template的问题,最终我们的解决方案就是:

@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;    }


你的关注和点赞是我最大的动力

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值