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 接这篇 在上文中,主要实现了可靠模式的 ...
随机推荐
- oracle-ORA-01567错误
删除日志4时将在线索1中保留少于两个日志文件
- windows.open window.location.href的用法和区别
window.location.href 只能在当前页面打开,不能用新窗口打开 windows.open("URL","窗口名称","窗口外观设定& ...
- php中括号定义数组
php5.3及之前的版本是不支持中括号定义数组的.5.4之后支持. 错误信息是,不识别“[”
- Mac终端打开AndroidStudio已创建模拟器
目的 偶尔我们只是想运行模拟器,并不想打开AndroidStudio,这时我们可以从终端找到emulator,通过emulator来启动指定名称的模拟器 步骤 1.找到emulator所在位置 fin ...
- Java练习 SDUT-2749_区域内点的个数
区域内点的个数 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description X晚上睡不着的时候不喜欢玩手机,也不喜欢打游戏,他喜欢数 ...
- sql —— having
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用.HAVING 子句可以让我们筛选分组后的各组数据. 原表: 我们可以对上面数据根据性别这个字段进行分组查询,分别 ...
- Atcoder Tenka1 Programmer Contest D: IntegerotS 【思维题,位运算】
http://tenka1-2017.contest.atcoder.jp/tasks/tenka1_2017_d 给定N,K和A1...AN,B1...BN,选取若干个Ai使它们的或运算值小于等于K ...
- 基于颜色的R2V软件快速矢量化
跟同学一起做SRTP,矢量化,作图的工作点名让我去做,人家说,谁让你是学地理信息的呢?哎,什么时候地理信息不再被别人当成制图画图的,我们专业就有希望了. 话虽然这么说,但工作还是要去做. (进入正题) ...
- Javascript中的定时调用函数setInterval()和setTimeout()
首先介绍这两个函数 一.setInterval() 按照指定的周期来调用函数或表达式,执行多次.(时间单位:ms) timer = setInterval("content =documen ...
- @bzoj - 5219@ [Lydsy2017省队十连测]最长路径
目录 @description@ @solution@ @accepted code@ @details@ @description@ 在Byteland一共有n个城市,编号依次为1到n,形成一个n个 ...