首先說一下背景,目前筆者的工作是物聯網方面的,裝置有對應的智慧運營平臺,平臺開發中建表的主鍵用的是Mybatis plus預設的雪花演算法來生成的,也就是分散式系統比較常用的雪花ID,技術棧就是常用的Spring boot+Spring could Alibaba,json工具用的是FastJson。
在開發的過程中遇到了一個問題:前端接收到的資料在回傳給後端的時候ID總是不對,仔細排查發現,前端接收到的資料的ID末尾兩到三位數字都變成了0。雪花ID的長度是19位數字,系統在bean中的ID用的是Long型別,資料庫建表用的是bigint,接收雪花ID自然沒有問題,但是前端的number型別只能接收16位數字,準確的說是:2的53次方減1,即為9007199254740991,所以回傳的ID不對是數字精度丟失的原因造成的。
知道了原因,解決方案也很簡單,後端傳給前端時把ID轉換位字串型別,前端接收字串就不會丟失精度了,前端把ID回傳給後端的時候,Spring的反序列化會自動為我們轉成Long型別,這麼一來就解決問題了。針對這一思路,樓主想到了兩種解決方案。
1、@JsonSerialize註解
JsonSerialize註解可以幫我們實現欄位值的序列化和反序列話,@JsonSerialize(using = ToStringSerializer.class),程式碼如下:
public class Device{ @ApiModelProperty(value = "物聯終端id")
@TableId(type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long deviceId; ...
}
在需要解決數字過長的欄位上新增sonSerialize註解就可以完美解決這一問題,但是開發的時候一定要注意,萬一漏掉很容易踩坑,所以在員工培訓的時候一定要有所交待。
2、過濾器
過濾器是一種一勞永逸的方法,筆者的專案引入的是fastjson依賴,fastjson可以通過SerializeFilters定製序列化,非常方便,先上程式碼:
package com.johanChan.app.config; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter; import java.util.ArrayList;
import java.util.List; /**
* @author JohanChan
* @ProjectName Demo
* @Description 與前端互動時對實體類中Long型別的ID欄位序列號
* @time 2021/6/23 11:30
*/
@Configuration
public class CustomFastJsonHttpMessageConverter {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig(); List<SerializerFeature> list = new ArrayList<>();
list.add(SerializerFeature.PrettyFormat);
list.add(SerializerFeature.WriteMapNullValue);
list.add(SerializerFeature.WriteNullStringAsEmpty);
list.add(SerializerFeature.WriteNullListAsEmpty);
list.add(SerializerFeature.QuoteFieldNames);
list.add(SerializerFeature.WriteDateUseDateFormat);
list.add(SerializerFeature.DisableCircularReferenceDetect);
list.add(SerializerFeature.WriteBigDecimalAsPlain); fastJsonConfig.setSerializerFeatures(list.toArray(new SerializerFeature[list.size()])); fastConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastConverter;
fastJsonConfig.setSerializeFilters(new ValueFilter() {
@Override
public Object process(Object object, String name, Object value) {
/*if ((StringUtils.endsWith(name, "Id") || StringUtils.equals(name,"id")) && value != null
&& value.getClass() == Long.class) {*/
if (value != null && value.getClass() == Long.class ) {
Long v = (Long) value;
if (v.toString().length() > 15) {
return String.valueOf(value);
}
}
return value;
}
});
return new HttpMessageConverters(converter);
}
}
在ValueFilter中自定義規則,long型別的變數值如果超過15位數則轉化成字串,前端的number型別可以接收16位數字,為什麼不用16位判斷呢?前面已經說過,前端雖然可以接收16位的數字,但最大是9007199254740991,如果用16位做判斷,就會有漏網之魚了。這種方法省心省力,基本上開發人員不需要再注意這種數字過大的問題,但是使用的時候也要有所考量,根據實際業務考慮系統中有沒有其他需求需要用較長的數字,統一用過濾器會不會受到影響。