Foreword
Before the burst of the development process problems encountered with rabbitmq template
sending 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
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); }
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.
jackson
Quite important, especially spring cloud
family bucket, feign
also with this, restTemplate
also used, as well as Spring MVC
in the httpmessageConverter
interest 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