文章目录


前提概要

首先,我们来说一下为什么要使用序列化?直接传输一个个的对象不就好了?

以我理解的三个角度来说明:

1.深拷贝,我们都知道浅拷贝的坏处,如果一个对象,使用=赋值,由于对象引用指向是不变的,一个对象改变了,千千万万个复制体都会随之改变。

2.持久化,通过序列化的方式可以将我们的对象以文件的形式访问,典型的就是将对象化为JSON格式,存入数据库或者其他文件中。

3.统一管理,由于我们采用相同的格式进行处理,那么下次序列化,反序列化都可以轻松的化为字符串,对象。

比较主流的序列化方式就是JSON格式,还有一种就是protobuf,速度更快,这里不多做介绍,主要是使用位运算,压缩等提高传输效率。

正反序列化算法要一致

我们以redis举例,一般我们都是使用的​​redisTemplate​​对象进行存取的,但是有一些坑是需要避免的。

下图所示,是​​redisTemplate​​默认的序列化方式,即jdk序列化,如果使用默认的在redis中查看会发现很多乱码,当然不推荐使用。

Java序列化踩坑指南_redis

如果要用的话,不如使用​​stringRedisTemplate​​类,采用的是String序列化,如下图所示。

Java序列化踩坑指南_redis_02

这里有一点需要注意,你不能存对象用​​redisTemplate​​,取对象使用​​redisTemplate​​,这样是会出问题的,总而言之,正反序列化算法要一直

以下给大家推荐一个通用的redis配置,无脑用就可以了。

@Configuration
public class RedisConfig {

@Bean
public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//key用字符串序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
//value采用json格式序列化 下同
redisTemplate.setValueSerializer(RedisSerializer.json());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setHashValueSerializer(RedisSerializer.json());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

总结一下,Spring中针对redis的4中序列化方式。

1.jdk序列化,默认的不好用,会出现乱码。

2.字符串序列化​​RedisSerializer.string()​​,自带utf8编码,强烈推荐

3.​​Jackson2JsonRedisSerializer​​,可能有用过,这个也不推荐,value会被化为​​LinkedHashMap​​。

4.​​RedisSerializer.json()​​,内部其实是创建了一个​​GenericJackson2JsonRedisSerializer​​对象,强烈推荐

RedisTemplate踩坑

如果我们要在上面的泛型里定义Long类型,如下所示。

RedisTemplate<String, Long> redisTemplate

那么在存取值的时候会出什么问题呢?

这里就不卖关子了。

如果使用该数字在整型范围内,需要用Integer类型接收,否则爆强类型错误。

如果超出整型范围,才可以用Long类型接收。

反序列化要小心构造方法

话不多说,直接上例子。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyResult {
private Integer code;
private Boolean success;

public MyResult(Integer code) {
this.code = code;
if (code == 200) {
success = true;
} else {
success = false;
}
}
}

可以看到,我们创建了结果返回类,我们想要的效果是,如果code=200,直接让​​success=true​​。

开始进行测试。

@Test
public void test2() throws JsonProcessingException {
String json1 = "{\n" +
"\"code\":200\n" +
"}";
String json2 = "{\n" +
"\"code\":404\n" +
"}";
ObjectMapper objectMapper = new ObjectMapper();
MyResult myResult1 = objectMapper.readValue(json1, MyResult.class);
MyResult myResult2 = objectMapper.readValue(json2, MyResult.class);
System.out.println(myResult1);
System.out.println(myResult2);
}

返回结果

MyResult(code=200, success=null)
MyResult(code=404, success=null)

有点出乎我们意料,这是为什么呢?

原因在于,jackson默认采用的是无参构造执行序列化,如果真要达到我们预期的效果,代码如下所示:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyResult {
private Integer code;
private Boolean success;

@JsonCreator //指定json反序列化构造
public MyResult(@JsonProperty("code") Integer code) {//指定json中的属性
this.code = code;
if (code == 200) {
success = true;
} else {
success = false;
}
}
}

打印结果如下所示

MyResult(code=200, success=true)
MyResult(code=404, success=false)

还有一个坑,这里就不上例子了,只是给出一点小建议。

不到万不得已,不要用枚举类作为DTO在服务中传输,一切返回值都最基本的构成都使用那8种数据类型就好了,枚举的坑会让你多写很多代码,还会出莫名的bug。


人生并不是一场谁掌控最多知识谁就能胜利的竞赛,最重要的是不断学习和成长。真正的损失是,因为害怕被人发现自己的无知,你完完全全逃避了生活,失去了太多宝贵的生命体验。