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是这样的:
@Data
public 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
@Data
public 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"}
解决办法
指定默认时区配置
@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);
}
在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有兴趣的同学,去看下面这个地方就可以了。

如果对JsonFormat的处理感兴趣,可以看下面的地方:
com.fasterxml.jackson.annotation.JsonFormat.Value#Value(com.fasterxml.jackson.annotation.JsonFormat) (打个断点在这里,然后跑个test就到这里了)

总结
差点忘了,针对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;
}
以上相关源码在:
https://gitee.com/ckl111/all-simple-demo-in-work/tree/master/jackson-demo
rabbitmq template发送的消息中,Date类型字段比当前时间晚了8小时的更多相关文章
- ORACLE中date类型字段的处理
(1)在英文版本的ORACLE中默认日期格式为'DD-MON-YY',例如'01-JAN-98' 在汉化的中文版本中ORACLE默认日期格式为'日-月-年',例如'21-8月-2003'或'21-8月 ...
- 关于Java读取mysql中date类型字段默认值'0000-00-00'的问题
今天在做项目过程中,查询一个表中数据时总碰到这个问题: java.sql.SQLException:Value '0000-00-00' can not be represented as ...
- ORACLE插入DATE类型字段
1 怎样在ORACLE中输入DATE类型的字段 insert into table_name (date_column) values(to_date('2006-06-04','yyyy-mm-dd ...
- 【java】jackson 中JsonFormat date类型字段的使用
为了便于date类型字段的序列化和反序列化,需要在数据结构的date类型的字段上用JsonFormat注解进行注解具体格式如下 @JsonFormat(pattern = "yyyy-MM- ...
- jackson 中JsonFormat date类型字段的使用
为了便于date类型字段的序列化和反序列化,需要在数据结构的date类型的字段上用JsonFormat注解进行注解具体格式如下 @JsonFormat(pattern = "yyyy-MM- ...
- SpringMVC返回Json,自定义Json中Date类型格式
http://www.cnblogs.com/jsczljh/p/3654636.html —————————————————————————————————————————————————————— ...
- C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法
原文:C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法 本人新手,在.Net中写WebAPI的时候,当接口返回的json数据含有日期时间类型的字段时, ...
- 解决Flink输出日志中时间比当前时间晚8个小时的问题
Flink安装在CentOS7上,默认时间是UTC时间,查看Flink日志,发现输出时间比当前时间晚8个小时. 通过如下命令,调整成北京时间 cp /usr/share/zoneinfo/Asia/S ...
- rabbitmq 不发送ack消息如何处理:rabbitmq可靠发送的自动重试机制
转载地址:http://www.jianshu.com/p/6579e48d18ae http://www.jianshu.com/p/4112d78a8753 接这篇 在上文中,主要实现了可靠模式的 ...
随机推荐
- thinkphp5.0 模板输出常用内容
1.在模板获取session和cookie等全局变量 {$Think.session.user_id}//输出$_SESSION['user_id']变量 2.获取请求信息 {$Request.con ...
- oracle加锁
锁 insert into TEST values(5); 加row exclusive锁,和row share相同,但也禁止用share方式加锁. Create index idx_test on ...
- bzoj2073 PRZ
Description 一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍 ...
- 2019-5-25-win10-uwp-win2d-入门-看这一篇就够了
title author date CreateTime categories win10 uwp win2d 入门 看这一篇就够了 lindexi 2019-5-25 20:0:52 +0800 2 ...
- 2018-10-19-Roslyn-使用-Directory.Build.props-文件定义编译
title author date CreateTime categories Roslyn 使用 Directory.Build.props 文件定义编译 lindexi 2018-10-19 18 ...
- 微信小程序中支持es7的async语法
最近在原生的微信小程序项目中需要把原来es6的promise方法改成es7的async await,这样代码看起来更直观,也方便以后的兄弟维护,但是改了代码之后项目就报错了. 提示的错误是:regen ...
- Node.js MVC模式+MongoDB实现学员管理系统
目录结构: 项目入口文件 /* Author:张波 */ /* 文件说明: 此文件是本项目的入口文件 启动这个项目,会先执行本文件中的代码 */ // 1. 引入模块 const http = req ...
- LeetCode73 Set Matrix Zeroes
题目: Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.(Me ...
- Java发送邮件的工具类
package com.csice.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNot ...
- DispatcherTimer 应用实例
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //实例化 Dispat ...