SpringCloud是搭建微服务的常用工具,Feign是微服务之间的通信的工具,在用Feign传递java.util.Date类型的参数时,会有时差问题,在CST(中国)时区运行时,这个时差为14小时。
错误原因——CTS的误解
- Feign客户端在进行通信时,会将Date类型对象转为String类型,如果这个时间是北京时间2019年2月19日20点30分,因为中国的时区叫做CTS,所以转化后的String为“Tue Feb 19 20:30:00 CST 2019”.
- 服务端将接收的String类型日期转换为Date类型,转换采用的是Date的默认构造器
new Date('Tue Feb 19 20:30:00 CST 2019')
,这里就是错误发生的时刻,因为CTS代表的时区其实有四个(Central Standard Time (USA) UT-6:00、Central Standard Time (Australia) UT+9:30、China Standard Time UT+8:00、Cuba Standard Time UT-4:00),同时表示美国,澳大利亚,中国,古巴四个国家的标准时间。根据JavaDoc,jvm会将CTS理解成了美国中部时区,因此造成了时区错误。
JVM见到CTS默认是美国的时区(UT-6:00),与中国时区(UT+8:00)相差14个小时。
解决办法
在客户端添加代码,规定Feign在将Date参数转化成String参数的格式:
import org.springframework.cloud.netflix.feign.FeignFormatterRegistrar;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class DateFormatRegister implements FeignFormatterRegistrar {
public DateFormatRegister() {
}
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(Date.class, String.class, new Date2StringConverter());
}
private class Date2StringConverter implements Converter<Date, String> {
@Override
public String convert(Date source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(source);
}
}
}
在服务端添加代码,规定SpringContext在String和Date时的用的转化器,让转化器知道我们在客户端配置的参数格式:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.annotation.PostConstruct;
import java.util.Date;
@Configuration
public class WebConfigBeans {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@PostConstruct
public void initEditableValidation() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter
.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService) initializer
.getConversionService();
genericConversionService.addConverter(String.class, Date.class, new String2DateConverter());
}
}
}
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
class String2DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
PS.由于这个问题在中国比较常见,所以这次百度到的方法要比Google更好一些 :)
网友评论