这几天在装修项目,用雪花算法生成主键ID,突然踩了一个天坑,当前端javascript取Long参数,参数值有点不太对!一、问题描述最近在改造内部管理系统的时候,发现一个巨大的坑,就是前端JS在获取后端Long参数的时候,精度丢失了!一开始用postman模拟接口请求非常困难。是正常的,但是用浏览器请求的时候就出问题了!问题复现:@RequestMapping("/queryUser")publicListqueryUser(){ListresultList=newArrayList<>();Useruser=newUser();//给一个长用户IDuser.setId(123456789012345678L);resultList.add(user);returnresultList;}打开浏览器请求接口,结果如下!用postman模拟接口请求,结果如下!一开始,还真没发现这个坑。结果在测试的时候发现前端传给后端的ID和数据库中保存的ID不一致,才发现JavaScript还是有这个sinkhole!由于JavaScript中Number类型的原因,它不能完全表示Long类型的数字,当Long的长度大于17位数字时会出现丢精度的问题。当我们把上面的用户ID改成19位后,我们再看看浏览器请求返回的结果。//设置用户ID,位数为19位user.setId(1234567890123456789l);浏览器请求结果!当返回结果超过17位时,后面的全部变为0!二、解决办法遇到这种情况,我该怎么办?第一种方法:在后台把long类型改成String类型,但是成本有点高,只要涉及到就需要改。第二种方法:使用工具将long类型转换成String类型,这种方法可以实现全局转换(推荐)第三种方法:前端处理(目前没有好的方法,不推荐)因为项目涉及比较多ofcode,long类型转String类型是不可能的,而且使用Long类型的方法很多,改起来风险很大,不推荐!理想的方法是使用aop代理拦截所有方法,统一处理返回参数,使用工具转换。过程如下!1.Jacksontoolstoserializeobjects我们可以使用Jacksontoolkit来序列化对象。首先在maven中添加必要的依赖:com.fasterxml.jackson.corejackson-core2.9。8com.fasterxml.jackson.core杰克逊注释2.9.8com.fasterxml.jackson.corejackson-databind2.9.8写一个转换工具类JsonUtil:publicclassJsonUtil{privatestaticfinalLoggerlog=LoggerFactory.getLogger(JsonUtil.class);privatestaticObjectMapperobjectMapper=newObjectMapper();privatestaticfinalStringDATE_FORMAT="yyyy-MM-ddHH:mm:ss";static{//对象的所有字段都包含在objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);//取消objectMapper.configure默认的转换时间戳(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);//忽略空bean转json的错误objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false);//设置为东八区objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));//统一日期格式objectMapper.setDateFormat(newSimpleDateFormat(DATE_FORMAT));//反序列化时,忽略json字符串中存在,java对象中不存在的情况,防止报错objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);//当sequence被json替换时,所有的long都会被改为stringobjectMapper.registerModule(newSimpleModule().addSerializer(Long.class,ToStringSerializer.instance).addSerializer(Long.TYPE,ToStringSerializer.instance));}/***对象序列化成json字符串*@paramobj*@param*@return*/publicstaticStringobjToStr(Tobj){if(null==obj){returnnull;}try{returnobjinstanceofString?(String)obj:objectMapper.writeValueAsString(obj);}catch(Exceptione){log.warn("objToStrerror:",e);returnnull;}}/***json字符串反序列化为对象*@paramstr*@paramclazz*@param*@return*/publicstaticTstrToObj(Stringstr,Classclazz){if(StringUtils.isBlank(str)||null==clazz){returnnull;}try{returnclazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz);}catch(Exceptione){log.warn("strToObjerror:",e);returnnull;}}/***json字符串反序列化为对象(数组)*@paramstr*@paramtypeReference*@param*@return*/publicstaticTstrToObj(Stringstr,TypeReferencetypeReference){if(StringUtils.isBlank(str)||null==typeReference){returnnull;}try{return(T)(typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference));}catch(Exceptione){log.warn("strToObjerror",e);returnnull;}}}接下来写一个实体类Person,用于测试:@DatapublicclassPersonimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateIntegerid;//长型参数privateLonguid;privateStringname;privateStringaddress;privateStringmobile;privateDatecreateTime;}最后写一个测试类来测试效果:publicstaticvoidmain(String[]args){Personperson=新人();人on.setId(1);person.setUid(1111L);person.setName("hello");person.setAddress("");System.out.println(JsonUtil.objToStr(person));}输出结果如下:最关键的一行代码是注册这个Convert类,这样所有的longs都可以改为string//当sequence改为json时,所有的longs都会改为stringSimpleModulesimpleModule=newSimpleModule();simpleModule.addSerializer(Long.class,ToStringSerializer.instance);simpleModule.addSerializer(Long.TYPE,ToStringSerializer.instance);objectMapper.registerModule(simpleModule);如果要格式化日期,可以全局设置。//全球统一日期格式objectMapper.setDateFormat(newSimpleDateFormat("yyyy-MM-ddHH:mm:ss"));也可以单独设置某个属性,比如将createTime属性格式化为yyyy-MM-dd,添加如下注解即可。@JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")privateDatecreateTime;工具转换类写好后,就很简单了,只需要将aop拦截方法返回的参数序列化即可,所有long都可以自动转换成字符串。2.SpringMVC配置如果是SpringMVC项目,操作也很简单。自定义一个继承自ObjectMapper的实现类:packagecom.example.util;/***继承ObjectMapper*/publicclassCustomObjectMapperextendsObjectMapper{publicCustomObjectMapper(){super();SimpleModulessimpleModule=newSimpleModule();simpleModule.addSerializer(Long.class,ToStringSerializer.instance);simpleModule.addSerializer(Long.TYPE,ToStringSerializer.instance);registerModule(simpleModule);}}在SpringMVC配置文件中添加如下配置:application/json;charset=UTF-8text/plain;charset=UTF-8
<-日期的统一转换->3。如果SpringBoot配置的是SpringBoot项目,操作类似写一个WebConfig配置类,从WebMvcConfigurer实现,重写configureMessageConverters方法:HttpMessageConverter>>list){MappingJackson2HttpMessageConverterjsonConverter=newMappingJackson2HttpMessageConverter();ObjectMapperobjectMapper=jsonConverter.getObjectMapper();//当sequence换成json时,将所有longs改成stringSimpleModulessimpleModule=newSimpleModule();simpleModule.addSerializer(Long.class,ToStringerSerializer.instance);simpleModule.addSerializer(Long.TYPE,ToStringSerializer.instance);objectMapper.registerModule(simpleModule);list.add(jsonConverter);}}3.总结在实际项目开发中,很多服务都是纯微服务开发的,不会使用SpringMVC。这种情况下,使用JsonUtil工具类来实现对象序列化可能是一个非常好的选择。