当前位置:首页 > 技术文章 > 正文内容

Spring Boot3 中解决返回接口 Long 类型数据精度损失问题全解析

zonemu2个月前 (08-24)技术文章23

在互联网软件开发领域,Spring Boot 作为一款广受欢迎的框架,极大地简化了 Java 应用程序的开发过程。然而,在实际开发中,开发人员常常会遇到各种棘手的问题。其中,当 Spring Boot 3 项目的接口返回 Long 类型数据时,前端出现精度损失的情况,就像一颗隐藏在程序深处的 “暗雷”,让不少开发人员头疼不已。今天,咱们就一起来深入剖析这个问题,并探讨出切实可行的解决方案。

问题根源探究

在深入探讨解决方案之前,我们必须先搞清楚为什么会出现 Long 类型数据精度损失的问题。在 JavaScript 中,Number 类型所能表示的安全整数范围是有限的,其范围为 - 2^53 到 2^53(即 - 9007199254740992 到 9007199254740992)。一旦后端返回的 Long 类型数据超出了这个范围,前端在处理时就极有可能出现精度丢失的情况。这是因为 JavaScript 采用 IEEE 754 双精度浮点数格式来存储数字,该格式无法精准表示超过 53 位的整数。

例如,假设我们在后端有一个 Java 对象,其中某个属性为 Long 类型,值为 1234567890123456789L。当这个对象通过接口以 JSON 格式返回给前端时,在前端 JavaScript 环境中,这个值可能就会被错误地解析为 1234567890123456000,从而导致数据出现偏差,影响整个业务逻辑的正确性。

解决方案详解

(一)使用注解方式(局部处理)

针对特定的字段或类,我们可以利用 Jackson 的 @JsonSerialize 注解,将 Long 类型的数据以字符串的形式传输,从而巧妙地避开前端的精度问题。下面来看具体的代码示例:

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

public class YourClass {
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    // 其他属性和方法
}

在上述代码中,通过@JsonSerialize(using = ToStringSerializer.class)这行代码,我们告诉 Jackson 在序列化id这个 Long 类型字段时,将其转换为字符串形式。这样一来,前端接收到的数据就是字符串,不会因为 JavaScript 的数字精度限制而出现精度丢失的情况。这种方式非常灵活,适用于我们只需要对部分特定字段进行处理的场景。

全局配置 ObjectMapper

倘若我们希望一劳永逸,在整个项目中全局解决 Long 类型数据精度丢失的问题,那么可以通过修改 Spring Boot 中ObjectMapper的配置来实现。具体有以下两种常见的实现方式:

通过配置类

首先,我们创建一个 Jackson 的配置类,在这个类中定义一个 Bean 来配置ObjectMapper。代码如下:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, new ToStringSerializer());
        simpleModule.addSerializer(Long.TYPE, new ToStringSerializer());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

在这个配置类中,我们创建了一个SimpleModule,并向其中添加了针对Long类型的序列化器ToStringSerializer。然后将这个模块注册到ObjectMapper中。经过这样的配置,Spring Boot 在进行 JSON 序列化时,所有的 Long 类型数据都会被自动转换为字符串类型,无需我们逐个字段去添加注解。

通过应用配置文件

除了通过配置类,我们还可以在application.properties或application.yml文件中添加配置,告诉 Jackson 将 Long 类型序列化为字符串。

如果是application.properties文件,添加如下配置:

spring.jackson.serialization.write-numbers-as-strings=true

若是application.yml文件,则配置如下:

spring:
  jackson:
    serialization:
      write-numbers-as-strings: true

这两种配置方式都能实现将 Long 类型字段全局序列化为字符串类型的目的。开发人员可以根据项目的实际情况和个人偏好来选择使用哪种方式。

方案对比与选择

(一)注解方式

优点

  • 灵活性高:可以针对具体的某个字段或类进行单独配置,对于一些局部性的需求,这种方式能够精准地满足。比如,在一个大型项目中,可能只有少数几个特定模块的 Long 类型字段需要特殊处理,此时使用注解方式就显得非常合适。
  • 对现有代码侵入性小:如果项目已经有大量的代码,并且大部分 Long 类型字段无需特殊处理,仅对个别字段使用注解,不会对整体代码结构造成太大的影响。

缺点

  • 工作量较大:当需要处理的 Long 类型字段较多时,就需要逐个字段或类去添加注解,这无疑会增加一定的开发工作量和维护成本。

(二)全局配置方式

优点

  • 一次配置,全局生效:只需要进行一次全局配置,项目中所有的 Long 类型数据在返回给前端时都会自动转换为字符串类型,大大提高了开发效率,尤其适用于整个项目中 Long 类型数据都可能存在精度问题的场景。
  • 便于维护:在后期维护过程中,如果需要对 Long 类型数据的处理方式进行调整,只需要在配置文件或配置类中修改一处即可,而不需要在大量的代码文件中逐个查找和修改注解。

缺点

  • 缺乏针对性:由于是全局配置,可能会对一些原本不需要将 Long 类型转换为字符串的场景也产生影响。比如,在某些特定的内部接口调用中,Long 类型数据的精度问题并不存在,此时全局配置可能会带来一些不必要的性能开销。

在实际项目中,我们需要根据具体的业务需求、项目规模以及代码结构等因素来综合考虑选择哪种解决方案。如果项目中大部分 Long 类型数据都需要避免精度损失,并且对全局配置的影响可以接受,那么全局配置ObjectMapper的方式会更加高效和便捷;而如果只是个别字段存在问题,或者对代码的灵活性要求较高,注解方式则是更好的选择。

总结

在 Spring Boot 3 开发中,解决返回接口 Long 类型数据精度损失问题是确保系统数据准确性和稳定性的重要一环。通过深入理解问题产生的根源,灵活运用注解方式和全局配置ObjectMapper这两种解决方案,我们能够有效地应对这一挑战。同时,在选择具体方案时,要充分权衡各种因素,以实现最佳的开发效果。希望本文所分享的内容能够帮助广大互联网软件开发人员在实际项目中顺利解决这一常见问题,提升项目的质量和用户体验。在不断变化的技术领域中,持续学习和探索,我们才能更好地应对各种新的挑战,创造出更加优秀的软件产品。

相关文章

八款值得尝试的精美的Linux发行版,你用过哪几款?

Linux发行版各式各样,每个发行版都有自己的特点,在这篇文章中,将会列出让一些另 Linux 用户印象最深刻且精美的 Linux 发行版,包括对初学者友好和流行的发行版。elementary OSe...

vue 3 学习笔记 (八)——provide 和 inject 用法及原理

在父子组件传递数据时,通常使用的是 props 和 emit,父传子时,使用的是 props,如果是父组件传孙组件时,就需要先传给子组件,子组件再传给孙组件,如果多个子组件或多个孙组件使用时,就需要传...

Gitlab 的使用和代码审查流程介绍

1、先简洁介绍下项目常用的信息-面板统计页面2、用户信息面板3、服务器信息4、项目信息5、重点介绍代码提交审核机制和授权合并机制开发人员推送代码的时候不能直接推送到master,否则就会报错。此时开发...

基于Docker构建安装Git/GitLab,以及制作springboot工程镜像

今天给大家分享的是《领先的开源自动化服务器Jenkins的应用实战》之基于Docker安装构建Git/GitLab版本控制与代码云存储的场所;使用Git管理项目,springboot工程制作镜像知识体...

Web开发的十佳HTML5响应式框架(h5响应式模板)

HTML5框架是一类有助于快速轻松创建响应式网站的程序包。这些HTML5框架有着能减轻编程任务和重复代码负担的神奇功能。关于HTML5的框架种类繁多,并且很瘦欢迎,因为它能允许开发人员花费更少的时间和...

基于 Go 泛型实现的工具库推荐(go2 泛型)

大家好,又见面了,我是 GitHub 精选君!今天要给大家推荐一个 GitHub 开源项目 samber/lo,该项目在 GitHub 有超过 9.5k Star,用一句话介绍该项目就是:“ A Lo...