前提概要
首先,我们来说一下为什么要使用序列化?直接传输一个个的对象不就好了?
以我理解的三个角度来说明:
1.深拷贝,我们都知道浅拷贝的坏处,如果一个对象,使用=赋值,由于对象引用指向是不变的,一个对象改变了,千千万万个复制体都会随之改变。
2.持久化,通过序列化的方式可以将我们的对象以文件的形式访问,典型的就是将对象转化为JSON格式,存入数据库或者其他文件中。
3.统一管理,由于我们采用相同的格式进行处理,那么下次序列化,反序列化都可以轻松的转化为字符串,对象。
比较主流的序列化方式就是JSON格式,还有一种就是protobuf,速度更快,这里不多做介绍,主要是使用位运算,压缩等提高传输效率。
正反序列化算法要一致
我们以redis举例,一般我们都是使用的redisTemplate 对象进行存取的,但是有一些坑是需要避免的。
下图所示,是redisTemplate 默认的序列化方式,即jdk序列化,如果使用默认的在redis中查看会发现很多乱码,当然不推荐使用。 如果要用的话,不如使用stringRedisTemplate 类,采用的是String序列化,如下图所示。 这里有一点需要注意,你不能存对象用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);
redisTemplate.setKeySerializer(RedisSerializer.string());
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
public MyResult(@JsonProperty("code") Integer code) {
this.code = code;
if (code == 200) {
success = true;
} else {
success = false;
}
}
}
打印结果如下所示
MyResult(code=200, success=true)
MyResult(code=404, success=false)
还有一个坑,这里就不上例子了,只是给出一点小建议。
不到万不得已,不要用枚举类作为DTO在服务中传输,一切返回值都最基本的构成都使用那8种数据类型就好了,枚举的坑会让你多写很多代码,还会出莫名的bug。
人生并不是一场谁掌控最多知识谁就能胜利的竞赛,最重要的是不断学习和成长。真正的损失是,因为害怕被人发现自己的无知,你完完全全逃避了生活,失去了太多宝贵的生命体验。
|