当前位置: 首页 > news >正文

前后端的双精度浮点数精度不一致问题解决方案,自定义Spring的消息转换器处理JSON转换

在 Java 中,Long 是一个 64 位的长整型,通常用于表示很大的整数。在后端,Long 类型的数据没有问题,因为 Java 本身使用的是 64 位的整数,可以表示的范围非常大。

但是,在前端 JavaScript 中,Long 类型的数据可能会出现精度问题。原因在于 JavaScript 使用的是 双精度浮点数(Number 类型) 来表示数字,而双精度浮点数只能精确表示 最大 53 位的整数,超过 53 位的整数会丢失精度。

具体来说,JavaScript 的 Number 类型可以表示的最大整数是 2^53 - 1,即 9007199254740991。而任何超过这个范围的整数(比如某些大于 53 位的 ID)就会出现精度丢失,无法精确表示。

假设你在后端生成了一个大于 9007199254740991 的 Long 类型 ID,例如:

Long id = 1234567890123456789L;
然后通过接口返回给前端,在前端的 JavaScript 中,数字会被转换为 Number 类型,可能会出现精度丢失的情况:
let id = 1234567890123456789;  // JavaScript 会将其转换为双精度浮点数
console.log(id);  // 输出: 1234567890123456700

可以看到,前端打印出来的数字已经失去了精度,因为超出了 JavaScript Number 类型能精确表示的范围。

解决思想是将Long转化为String,但解决方式有很多,要么你在对应数据库表的实体类(比如User)的id类型设置为String,要么可以进行如下配置:

package com.xxx.xxxxx.common;import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;/*** 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]*/
public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";public JacksonObjectMapper() {super();//收到未知属性时不报异常this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时,属性不存在的兼容处理this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);SimpleModule simpleModule = new SimpleModule().addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))).addSerializer(BigInteger.class, ToStringSerializer.instance).addSerializer(Long.class, ToStringSerializer.instance).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//注册功能模块 例如,可以添加自定义序列化器和反序列化器this.registerModule(simpleModule);}
}
package com.xxx.xxxxx.config;import com.xxx.xxxxx.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import java.util.List;@Slf4j
@Configuration
// 继承自 WebMvcConfigurationSupport,这表示让这个类成为 Spring MVC 配置的一部分,允许你定制 Spring MVC 的配置
public class WebConfig extends WebMvcConfigurationSupport {@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("资源映射开始。。");registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");}@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {// 添加一个转换器,除自带八大转换器外,将Long装成StringMappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();converter.setObjectMapper(new JacksonObjectMapper());// 优先级设置到最高,让spring优先用咱自己自定义的转换器converters.add(0,converter);}
}

相关文章:

  • [杂学笔记]浏览器多进程与多线程架构、wstring类型、哈希表、红黑树与哈希表的对比、C++标准库Random类
  • 每日算法 -【Swift 算法】寻找字符串中最长回文子串(三种经典解法全解析)
  • 工业物联网网关在变电站远程监控中的安全传输解决方案
  • vscode离线安装组件工具vsix
  • Java安全-Servlet内存马
  • 计算机网络--第一章(下)
  • 贪心算法 Part04
  • Python数据分析实战:Pandas高效处理Excel数据指南
  • DataOutputStream DataInputStream转换流
  • Mysql-数据闪回工具MyFlash
  • IOMMU打开 关闭
  • 【76. 最小覆盖子串】
  • Java使用mybatis-plus做查询时LocalDateTime报错处理方案
  • Oracle Enqueue Names
  • Nginx配置同一端口不同域名或同一IP不同端口
  • DSP定时器的计算
  • [每日一题] 3356. 零数组变换ii
  • C++初阶-vector的模拟实现2
  • WebRTC与RTSP|RTMP的技术对比:低延迟与稳定性如何决定音视频直播的未来
  • 汽车电子电气架构诊断功能开发全流程解析
  • 北京工程信息网站/石家庄网络推广平台