在开发中,后端经常需要处理一些大数值的 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
类型字段生效。
@Configurationpublic 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);
具体工作原理
Spring Boot 自动配置 ObjectMapper
Spring Boot 自带 Jackson 作为默认的 JSON 处理库,并在后台自动配置了 ObjectMapper
。当你在项目中引入 Jackson 时,Spring Boot 会自动加载一个全局共享的 ObjectMapper
实例,用于所有 JSON 序列化和反序列化操作。
控制器的自动 JSON 转换
在 Spring MVC 中,控制器(@RestController
或 @Controller
)中的方法返回值会被自动转换为 JSON 格式,这个转换由 Spring Boot 的 ObjectMapper
处理。例如:
@RestControllerpublic 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
,就能在全局范围内应用此配置,使得后端传递到前端的数据在大数值时也能准确无误。