关于前端接收雪花算法id丢失精度问题,主要原因是因为前端number类型是16位而雪花算法生成的Long类型是19位,所以在传递的时候就出现了丢失最后四位的现象(实际是还有三位接收不到,所以四舍五入了,就影响了最后四位).
解决这个问题可以将19位的Long类型转换为string类型进行传递
将Long类型转换为string类型传递,
又因为一个对象或则值,在不同模块或则环境传递的时候会进行序列化然后传递(java底层原理),
所以需要将序列化更改即可,在这里有两种方法
第一种 将所有的Long类型在传递的时候都更改为string类型
第一步 先实现序列化时的方法
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
第二步
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 设置静态资源映射
* @param registry
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态资源映射...");
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
*/
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
}
第二种方法 根据自定义注解或则参数名为id进行替换
自定义注解
import com.fasterxml.jackson.annotation.JacksonAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@JacksonAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface IdEncrypt {
}
序列化时更换规则
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ConfusionSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
try {
if (value != null) {
jsonGenerator.writeString(value.toString());
return;
}
}catch (Exception e){
e.printStackTrace();
}
serializers.defaultSerializeValue(value, jsonGenerator);
}
}
序列化的时候判定
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.ArrayList;
import java.util.List;
public class ConfusionSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
List<BeanPropertyWriter> newWriter = new ArrayList<>();
for(BeanPropertyWriter writer : beanProperties){
String name = writer.getType().getTypeName();
if(null == writer.getAnnotation(IdEncrypt.class)
&& !writer.getName().equalsIgnoreCase("id")){
newWriter.add(writer);
} else {
writer.assignSerializer(new ConfusionSerializer());
newWriter.add(writer);
}
}
return newWriter;
}
}
反序列化
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class ConfusionDeserializer extends JsonDeserializer<Object> {
JsonDeserializer<Object> deserializer = null;
JavaType type =null;
public ConfusionDeserializer(JsonDeserializer<Object> deserializer, JavaType type){
this.deserializer = deserializer;
this.type = type;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
try {
if(type!=null){
if(type.getTypeName().contains("Long")){
return Long.valueOf(p.getValueAsString());
}
if(type.getTypeName().contains("Integer")){
return Integer.valueOf(p.getValueAsString());
}
}
return IdsUtils.decryptLong(p.getValueAsString());
}catch (Exception e){
if(deserializer!=null){
return deserializer.deserialize(p,ctxt);
}else {
return p.getCurrentValue();
}
}
}
}
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import java.util.Iterator;
public class ConfusionDeserializerModifier extends BeanDeserializerModifier {
@Override
public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config, final BeanDescription beanDescription, final BeanDeserializerBuilder builder) {
Iterator it = builder.getProperties();
while (it.hasNext()) {
SettableBeanProperty p = (SettableBeanProperty) it.next();
if ((null != p.getAnnotation(IdEncrypt.class)||p.getName().equalsIgnoreCase("id"))) {
JsonDeserializer<Object> current = p.getValueDeserializer();
builder.addOrReplaceProperty(p.withValueDeserializer(new ConfusionDeserializer(p.getValueDeserializer(),p.getType())), true);
}
}
return builder;
}
}
注册当前文件
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ConfusionModule extends Module {
public final static String MODULE_NAME = "jackson-confusion-encryption";
public final static Version VERSION = new Version(1,0,0,null,"heima",MODULE_NAME);
@Override
public String getModuleName() {
return MODULE_NAME;
}
@Override
public Version version() {
return VERSION;
}
@Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new ConfusionSerializerModifier());
context.addBeanDeserializerModifier(new ConfusionDeserializerModifier());
}
/**
* 注册当前模块
* @return
*/
public static ObjectMapper registerModule(ObjectMapper objectMapper){
//CamelCase策略,Java对象属性:personId,序列化后属性:persionId
//PascalCase策略,Java对象属性:personId,序列化后属性:PersonId
//SnakeCase策略,Java对象属性:personId,序列化后属性:person_id
//KebabCase策略,Java对象属性:personId,序列化后属性:person-id
// 忽略多余字段,抛错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
return objectMapper.registerModule(new ConfusionModule());
}
}
打开配置
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class InitJacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper = ConfusionModule.registerModule(objectMapper);
return objectMapper;
}
}