1,问题场景
后端返回的Long类型的数据,超10000000000000000,前端处理的时候,数据被截断了。比如tchId: 11073477511443988481, 前端根据tchId获取下一环节信息的时候,传的tchId变成了11073477511443988400,导致报错。
主要是因为JavaScript中的Number类型无法精确表示超过其安全整数范围(即-9007199254740991(-2^53 + 1)到9007199254740991(2^53 - 1))的整数值。因此,当后端Java的Long类型数据超过这个范围时,前端JavaScript在解析这些数据时就会丢失精度。
因此当Long类型超过的时候,需要把Long类型,转成字符串返回给前端。
2,处理:
1,处理方法1:字段加注解
可以使用Jackson库的@JsonFormat注解来将Long类型字段序列化为字符串类型。这样,在数据传输到前端时,就会以字符串的形式进行传输,从而避免了精度丢失的问题,如代码字段返回转换成字符串。
import com.fasterxml.jackson.annotation.JsonFormat;public class Tache {@JsonFormat(shape = JsonFormat.Shape.STRING)private Long tchld;
}
这种就只适合指定的字段,不通用。如果要设置未全局要怎么做?
2,处理方法2:全局处理
为了更加便捷地解决这个问题,使用全局配置,将所有Long类型的数据都转换为字符串类型。通过配置Jackson的ObjectMapper来实现
引用:
implementation 'com.fasterxml.jackson.core:jackson-core:2.13.5'
判断处理:
public class StringSerializer extends StdSerializer<Object> {private static Logger logger = LoggerFactory.getLogger(StringSerializer.class);public final static StringSerializer instance = new StringSerializer();public StringSerializer() {super(Object.class);}public StringSerializer(Class<?> handledType) {super(handledType, false);}@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {if (value != null && value instanceof Long) {if (((Long) value).longValue() >= 10000000000000000L) {gen.writeString(value.toString());} else {gen.writeNumber((Long) value);}}}
}
配置:
@Configuration
@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}@Beanpublic ObjectMapper jacksonObjectMapperCustomization() {TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().timeZone(timeZone);return builder.build();}@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter);MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(jacksonObjectMapperCustomization());ObjectMapper objectMapper = new ObjectMapper();SimpleModule simpleModule = new SimpleModule();simpleModule.addSerializer(Long.class, StringSerializer.instance);simpleModule.addSerializer(Long.TYPE, StringSerializer.instance);objectMapper.registerModule(simpleModule);objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);jackson2HttpMessageConverter.setObjectMapper(objectMapper);converters.add(jackson2HttpMessageConverter);converters.add(new ByteArrayHttpMessageConverter());converters.add(new FormHttpMessageConverter());converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));}@Beanpublic LocaleChangeInterceptor localeChangeInterceptor() {LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();localeChangeInterceptor.setParamName("LOCALE");return localeChangeInterceptor;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(localeChangeInterceptor());}@Beanprotected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {return new RequestMappingHandlerMapping() {@Overrideprotected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class)|| (AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)&& !AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class)));}};}@Beanpublic WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();allEndpoints.addAll(webEndpoints);allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());String basePath = webEndpointProperties.getBasePath();EndpointMapping endpointMapping = new EndpointMapping(basePath);boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);}private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));}
}
处理的代码:
simpleModule.addSerializer(Long.class, StringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, StringSerializer.instance);
总结:
如果是只有某些指定数据进行转换,就直接加注解的方式,要全局的话,就用全局的配置去处理。