自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

后端的Long型參數(shù),讓阿粉踩了個(gè)大坑

開(kāi)發(fā) 后端
最近幾天一直在改造工程,采用雪花算法生成主鍵ID,突然踩到一個(gè)天坑,前端 JavaScript 在取 Long 型參數(shù)時(shí),參數(shù)值有點(diǎn)不太對(duì)!

最近幾天一直在改造工程,采用雪花算法生成主鍵ID,突然踩到一個(gè)天坑,前端 JavaScript 在取 Long 型參數(shù)時(shí),參數(shù)值有點(diǎn)不太對(duì)!

[[340323]]

一、問(wèn)題描述

最近在改造內(nèi)部管理系統(tǒng)的時(shí)候, 發(fā)現(xiàn)了一個(gè)巨坑,就是前端 JS 在獲取后端 Long 型參數(shù)時(shí),出現(xiàn)精度丟失!

起初,用 postman 模擬接口請(qǐng)求,都很正常,但是用瀏覽器請(qǐng)求的時(shí)候,就出現(xiàn)問(wèn)題了!

問(wèn)題復(fù)現(xiàn):

  1. @RequestMapping("/queryUser") 
  2. public List<User> queryUser(){ 
  3.     List<User> resultList = new ArrayList<>(); 
  4.     User user = new User(); 
  5.     //賦予一個(gè)long型用戶(hù)ID 
  6.     user.setId(123456789012345678L); 
  7.     resultList.add(user); 
  8.     return resultList; 

打開(kāi)瀏覽器,請(qǐng)求接口,結(jié)果如下!

用 postman 模擬接口請(qǐng)求,結(jié)果如下!

剛開(kāi)始的時(shí)候,還真沒(méi)發(fā)現(xiàn)這個(gè)坑,結(jié)果當(dāng)進(jìn)行測(cè)試的時(shí)候,才發(fā)現(xiàn)前端傳給后端的ID,與數(shù)據(jù)庫(kù)中存的ID不一致,才發(fā)現(xiàn) JavaScript 還有這個(gè)天坑!

由于 JavaScript 中 Number 類(lèi)型的自身原因,并不能完全表示 Long 型的數(shù)字,在 Long 長(zhǎng)度大于17位時(shí)會(huì)出現(xiàn)精度丟失的問(wèn)題。

當(dāng)我們把上面的用戶(hù) ID 改成 19 位的時(shí)候,我們?cè)賮?lái)看看瀏覽器請(qǐng)求返回的結(jié)果。

  1. //設(shè)置用戶(hù)ID,位數(shù)為19位 
  2. user.setId(1234567890123456789l); 

瀏覽器請(qǐng)求結(jié)果!

當(dāng)返回的結(jié)果超過(guò)17位的時(shí)候,后面的全部變成0!

二、解決辦法

遇到這種情況,應(yīng)該怎么辦呢?

  • 第一種辦法:在后臺(tái)把 long 型改為String類(lèi)型,但是代價(jià)有點(diǎn)大,只要涉及到的地方都需要改
  • 第二種辦法:使用工具進(jìn)行轉(zhuǎn)化把 long 型改為String類(lèi)型,這種方法可以實(shí)現(xiàn)全局轉(zhuǎn)化(推薦)
  • 第三種辦法:前端進(jìn)行處理(目前沒(méi)有很好的辦法,不推薦)

因?yàn)轫?xiàng)目涉及到的代碼非常多,所以不可能把 long 型改為 String 類(lèi)型,而且使用 Long 類(lèi)型的方法非常多,改起來(lái)風(fēng)險(xiǎn)非常大,所以不推薦使用!

最理想的方法,就是使用aop代理攔截所有的方法,對(duì)返回參數(shù)進(jìn)行統(tǒng)一處理,使用工具進(jìn)行轉(zhuǎn)化,過(guò)程如下!

1. Jackson 工具序列化對(duì)象

我們可以使用Jackson工具包來(lái)實(shí)現(xiàn)對(duì)象序列化。

首先在 maven 中添加必須的依賴(lài):

  1. <!--jackson依賴(lài)--> 
  2. <dependency> 
  3.     <groupId>com.fasterxml.jackson.core</groupId> 
  4.     <artifactId>jackson-core</artifactId> 
  5.     <version>2.9.8</version> 
  6. </dependency> 
  7. <dependency> 
  8.     <groupId>com.fasterxml.jackson.core</groupId> 
  9.     <artifactId>jackson-annotations</artifactId> 
  10.     <version>2.9.8</version> 
  11. </dependency> 
  12. <dependency> 
  13.     <groupId>com.fasterxml.jackson.core</groupId> 
  14.     <artifactId>jackson-databind</artifactId> 
  15.     <version>2.9.8</version> 
  16. </dependency> 

編寫(xiě)一個(gè)轉(zhuǎn)化工具類(lèi)JsonUtil:

  1. public class JsonUtil { 
  2.  
  3.     private static final Logger log = LoggerFactory.getLogger(JsonUtil.class); 
  4.  
  5.     private static ObjectMapper objectMapper = new ObjectMapper(); 
  6.     private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"
  7.  
  8.     static { 
  9.         // 對(duì)象的所有字段全部列入 
  10.         objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); 
  11.         // 取消默認(rèn)轉(zhuǎn)換timestamps形式 
  12.         objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 
  13.         // 忽略空bean轉(zhuǎn)json的錯(cuò)誤 
  14.         objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 
  15.         //設(shè)置為東八區(qū) 
  16.         objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); 
  17.         // 統(tǒng)一日期格式 
  18.         objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT)); 
  19.         // 反序列化時(shí),忽略在json字符串中存在, 但在java對(duì)象中不存在對(duì)應(yīng)屬性的情況, 防止錯(cuò)誤 
  20.         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 
  21.         // 序列換成json時(shí),將所有的long變成string 
  22.         objectMapper.registerModule(new SimpleModule().addSerializer(Long.class, ToStringSerializer.instance).addSerializer(Long.TYPE, ToStringSerializer.instance)); 
  23.     } 
  24.  
  25.     /** 
  26.      * 對(duì)象序列化成json字符串 
  27.      * @param obj 
  28.      * @param <T> 
  29.      * @return 
  30.      */ 
  31.     public static <T> String objToStr(T obj) { 
  32.         if (null == obj) { 
  33.             return null; 
  34.         } 
  35.  
  36.         try { 
  37.             return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); 
  38.         } catch (Exception e) { 
  39.             log.warn("objToStr error: ", e); 
  40.             return null; 
  41.         } 
  42.     } 
  43.  
  44.     /** 
  45.      * json字符串反序列化成對(duì)象 
  46.      * @param str 
  47.      * @param clazz 
  48.      * @param <T> 
  49.      * @return 
  50.      */ 
  51.     public static <T> T strToObj(String str, Class<T> clazz) { 
  52.         if (StringUtils.isBlank(str) || null == clazz) { 
  53.             return null; 
  54.         } 
  55.  
  56.         try { 
  57.             return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz); 
  58.         } catch (Exception e) { 
  59.             log.warn("strToObj error: ", e); 
  60.             return null; 
  61.         } 
  62.     } 
  63.  
  64.     /** 
  65.      * json字符串反序列化成對(duì)象(數(shù)組) 
  66.      * @param str 
  67.      * @param typeReference 
  68.      * @param <T> 
  69.      * @return 
  70.      */ 
  71.     public static <T> T strToObj(String str, TypeReference<T> typeReference) { 
  72.         if (StringUtils.isBlank(str) || null == typeReference) { 
  73.             return null; 
  74.         } 
  75.  
  76.         try { 
  77.             return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); 
  78.         } catch (Exception e) { 
  79.             log.warn("strToObj error", e); 
  80.             return null; 
  81.         } 
  82.     } 

緊接著,編寫(xiě)一個(gè)實(shí)體類(lèi)Person,用于測(cè)試:

  1. @Data 
  2. public class Person implements Serializable { 
  3.  
  4.     private static final long serialVersionUID = 1L
  5.  
  6.     private Integer id; 
  7.  
  8.     //Long型參數(shù) 
  9.     private Long uid; 
  10.     private String name; 
  11.     private String address; 
  12.     private String mobile; 
  13.  
  14.     private Date createTime; 

最后,我們編寫(xiě)一個(gè)測(cè)試類(lèi)測(cè)試一下效果:

  1. public static void main(String[] args) { 
  2.     Person person = new Person(); 
  3.     person.setId(1); 
  4.     person.setUid(1111L); 
  5.     person.setName("hello"); 
  6.     person.setAddress(""); 
  7.     System.out.println(JsonUtil.objToStr(person)); 

輸出結(jié)果如下:

其中最關(guān)鍵一行代碼,是注冊(cè)了這個(gè)轉(zhuǎn)換類(lèi),從而實(shí)現(xiàn)將所有的 long 變成 string。

  1. // 序列換成json時(shí),將所有的long變成string 
  2. SimpleModule simpleModule = new SimpleModule(); 
  3. simpleModule.addSerializer(Long.class, ToStringSerializer.instance); 
  4. simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); 
  5. objectMapper.registerModule(simpleModule); 

如果想對(duì)某個(gè)日期進(jìn)行格式化,可以全局設(shè)置。

  1. //全局統(tǒng)一日期格式 
  2. objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); 

也可以,單獨(dú)對(duì)某個(gè)屬性進(jìn)行設(shè)置,例如對(duì)createTime屬性格式化為yyyy-MM-dd,只需要加上如下注解即可。

  1. @JsonFormat(pattern="yyyy-MM-dd"timezone="GMT+8"
  2. private Date createTime; 

工具轉(zhuǎn)化類(lèi)寫(xiě)好之后,就非常簡(jiǎn)單了,只需要對(duì) aop 攔截的方法返回的參數(shù),進(jìn)行序列化就可以自動(dòng)實(shí)現(xiàn)將所有的 long 變成 string。

2. SpringMVC 配置

如果是 SpringMVC 項(xiàng)目,操作也很簡(jiǎn)單。

自定義一個(gè)實(shí)現(xiàn)類(lèi),繼承自O(shè)bjectMapper:

  1. package com.example.util; 
  2.  
  3. /** 
  4.  * 繼承ObjectMapper 
  5.  */ 
  6. public class CustomObjectMapper extends ObjectMapper { 
  7.  
  8.     public CustomObjectMapper() { 
  9.         super(); 
  10.         SimpleModule simpleModule = new SimpleModule(); 
  11.         simpleModule.addSerializer(Long.class, ToStringSerializer.instance); 
  12.         simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); 
  13.         registerModule(simpleModule); 
  14.     } 

在 SpringMVC 的配置文件中加上如下配置:

  1. <mvc:annotation-driven > 
  2.     <mvc:message-converters> 
  3.         <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
  4.             <constructor-arg index="0" value="utf-8" /> 
  5.             <property name="supportedMediaTypes"> 
  6.                 <list> 
  7.                     <value>application/json;charset=UTF-8</value> 
  8.                     <value>text/plain;charset=UTF-8</value> 
  9.                 </list> 
  10.             </property> 
  11.         </bean>           
  12.         <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> 
  13.             <property name="objectMapper"> 
  14.                 <bean class="com.example.util.CustomObjectMapper"> 
  15.                     <property name="dateFormat"> 
  16.                         <-對(duì)日期進(jìn)行統(tǒng)一轉(zhuǎn)化-> 
  17.                         <bean class="java.text.SimpleDateFormat"> 
  18.                             <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" /> 
  19.                         </bean> 
  20.                     </property> 
  21.                 </bean> 
  22.             </property> 
  23.         </bean> 
  24.     </mvc:message-converters> 
  25. </mvc:annotation-driven> 

3. SpringBoot 配置

如果是 SpringBoot 項(xiàng)目,操作也類(lèi)似。

編寫(xiě)一個(gè)WebConfig配置類(lèi),并實(shí)現(xiàn)自WebMvcConfigurer,重寫(xiě)configureMessageConverters方法:

  1. /** 
  2.  * WebMvc配置 
  3.  */ 
  4. @Configuration 
  5. @Slf4j 
  6. @EnableWebMvc 
  7. public class WebConfig implements WebMvcConfigurer { 
  8.  
  9.     /** 
  10.      *添加消息轉(zhuǎn)化類(lèi) 
  11.      * @param list 
  12.      */ 
  13.     @Override 
  14.     public void configureMessageConverters(List<HttpMessageConverter<?>> list) { 
  15.         MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); 
  16.         ObjectMapper objectMapper = jsonConverter.getObjectMapper(); 
  17.         //序列換成json時(shí),將所有的long變成string 
  18.         SimpleModule simpleModule = new SimpleModule(); 
  19.         simpleModule.addSerializer(Long.class, ToStringSerializer.instance); 
  20.         simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); 
  21.         objectMapper.registerModule(simpleModule); 
  22.         list.add(jsonConverter); 
  23.     } 

三、總結(jié)

在實(shí)際的項(xiàng)目開(kāi)發(fā)中,很多服務(wù)都是純微服務(wù)開(kāi)發(fā),沒(méi)有用到SpringMVC,在這種情況下,使用JsonUtil工具類(lèi)實(shí)現(xiàn)對(duì)象序列化,可能是一個(gè)非常好的選擇。

 

責(zé)任編輯:趙寧寧 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2024-09-24 13:31:33

2023-10-26 07:29:06

mongodb十六進(jìn)制ID

2024-01-24 12:09:33

代碼Lodash前端

2025-01-16 16:16:53

2024-12-09 08:25:47

Springsave方法

2022-08-28 20:07:17

Docker后端

2020-06-05 07:42:16

參數(shù)驗(yàn)證合法

2021-01-08 07:38:15

代碼功能調(diào)用

2020-03-27 10:20:05

安全眾測(cè)滲透測(cè)試網(wǎng)絡(luò)安全

2015-05-11 10:39:19

2024-08-30 11:40:19

2021-05-07 07:59:52

WebFluxSpring5系統(tǒng)

2022-03-15 17:35:20

電商系統(tǒng)架構(gòu)

2020-03-09 10:21:12

Java集合類(lèi) Guava

2021-08-19 07:34:55

RabbitMQLinuxWindows

2020-09-06 10:02:32

項(xiàng)目管理戰(zhàn)略目標(biāo)CIO

2020-11-06 07:35:09

微信支付支付寶

2020-10-19 06:47:05

爬蟲(chóng)數(shù)據(jù)Jsoup

2020-07-09 07:54:35

ThreadPoolE線(xiàn)程池

2020-10-30 07:43:35

Jenkins配置前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)