long类型后端到前端精度丢失问题
在开发中,后端经常需要处理一些大数值的 long
类型数据(id等)。但当这些数据通过接口传递到前端时,可能会出现精度丢失的问题。
原因:
javascript 的 number
类型遵循 ieee 754 双精度浮点数标准,只能精确表示范围在 -(2^53 - 1)
到 2^53 - 1
之间的整数(约等于 -9007199254740991
到 9007199254740991
)。
这意味着,任何超过这个范围的整数在 javascript 中都无法精确表示。
例如:
console.log(9007199254740991); // 输出:9007199254740991(准确) console.log(9007199254740992); // 输出:9007199254740992(准确) console.log(9007199254740993); // 输出:9007199254740992(精度丢失)
在后端(例如 java)中,long
类型的范围是 -2^63
到 2^63 - 1
,即 -9223372036854775808
到 9223372036854775807
。
这种数值在 java 中可以被精确表示,但当通过 json 传递到前端的 javascript 环境时,由于 javascript 数字精度限制,往往会出现 精度丢失 的问题。
典型场景
当后端返回一个 id 值 1234567890123456789
,前端可能会收到 1234567890123456000
,这样就导致了数据的错误。
解决方案
方案一:后端将 long 类型序列化为 string 类型
一种简单有效的解决方案是,将后端的 long
类型值序列化为 string
,这样前端会将其当作字符串处理,从而避免了精度丢失的问题。下面是具体的实现方法。
1. 使用@jsonserialize注解单独处理字段
如果只是需要处理某个特定字段(例如 id
),可以在字段上添加 @jsonserialize
注解,指定 jackson 使用 tostringserializer
序列化器。
import com.fasterxml.jackson.databind.annotation.jsonserialize; import com.fasterxml.jackson.databind.ser.std.tostringserializer; public class passengerqueryresp { @jsonserialize(using = tostringserializer.class) private long id; private long memberid; private string name; private string idcard; // getter 和 setter }
配置了这个 objectmapper
之后,所有的 long
类型字段在序列化时都会被转换为字符串格式,不再需要逐个字段添加注解。
2. 配置全局的objectmapper设置
如果需要对所有 long
类型的字段进行字符串处理,可以在 spring boot 的配置类中定义一个全局 objectmapper
bean,使用 simplemodule
注册 tostringserializer
,这会对所有 long
类型字段生效。
@configuration public class jacksonconfig { @bean public objectmapper jacksonobjectmapper(jackson2objectmapperbuilder builder) { objectmapper objectmapper = builder.createxmlmapper(false).build(); simplemodule simplemodule = new simplemodule(); simplemodule.addserializer(long.class, tostringserializer.instance); objectmapper.registermodule(simplemodule); return objectmapper; } }
配置类的作用详解
在 jacksonconfig
类中,通过以下代码将 long
类型序列化为 string
:
simplemodule simplemodule = new simplemodule(); simplemodule.addserializer(long.class, tostringserializer.instance); objectmapper.registermodule(simplemodule);
具体工作原理
1.spring boot 自动配置 objectmapper
- spring boot 自带 jackson 作为默认的 json 处理库,并在后台自动配置了
objectmapper
。 - 当你在项目中引入 jackson 时,spring boot 会自动加载一个全局共享的
objectmapper
实例,用于所有 json 序列化和反序列化操作。
2.控制器的自动 json 转换
- 在 spring mvc 中,控制器(
@restcontroller
或@controller
)中的方法返回值会被自动转换为 json 格式,这个转换由 spring boot 的objectmapper
处理。 - 例如:
@restcontroller public class usercontroller { @getmapping("/user") public user getuser() { return new user(123456789012345l, "alice"); } }
- 当
user
对象被返回时,spring mvc 会自动使用objectmapper
将其序列化为 json 响应发送给客户端。 - 如果你配置了
jacksonconfig
类,spring 会使用你配置的objectmapper
,因此所有long
类型字段会自动以字符串形式输出,避免精度丢失。
3.自定义 objectmapper
配置的全局效果
通过自定义 jacksonconfig
,实际上是让 spring boot 使用你定义的 objectmapper
bean 作为全局配置。
spring boot 会自动检测应用中的 objectmapper
bean,并将其用于所有 json 序列化和反序列化操作,这包括:
@restcontroller
返回的对象。@requestbody
和@responsebody
注解处理的 json 请求和响应。- 其他 spring boot 内部或第三方库需要 json 处理的地方。
总结
在后端开发中处理 long
类型数据时,注意 javascript 的精度限制十分重要。
通过配置 jackson 的 objectmapper
,可以让所有 long
类型字段序列化为字符串,从而避免精度丢失的问题。
spring boot 的自动化配置特性让我们无需手动调用 objectmapper
,就能在全局范围内应用此配置,使得后端传递到前端的数据在大数值时也能准确无误。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论