重溫序列化概念
- 序列化:把對(duì)象轉(zhuǎn)化為可傳輸?shù)淖止?jié)序列過(guò)程稱(chēng)為序列化。
- 反序列化:把字節(jié)序列還原為對(duì)象的過(guò)程稱(chēng)為反序列化。
為什么需要序列化
- 序列化最終的目的是為了對(duì)象可以跨平臺(tái)存儲(chǔ)庄撮,和進(jìn)行網(wǎng)絡(luò)傳輸晓勇。而我們進(jìn)行跨平臺(tái)存儲(chǔ)和網(wǎng)絡(luò)傳輸?shù)姆绞骄褪荌O,而我們的IO支持的數(shù)據(jù)格式就是字節(jié)數(shù)組悔叽。
- 因?yàn)槲覀儐畏矫娴闹话褜?duì)象轉(zhuǎn)成字節(jié)數(shù)組還不行,因?yàn)闆](méi)有規(guī)則的字節(jié)數(shù)組我們是沒(méi)辦法把對(duì)象的本來(lái)面目還原回來(lái)的爵嗅,所以我們必須在把對(duì)象轉(zhuǎn)成字節(jié)數(shù)組的時(shí)候就制定一種規(guī)則(序列化)娇澎,那么我們從IO流里面讀出數(shù)據(jù)的時(shí)候再以這種規(guī)則把對(duì)象還原回來(lái)(反序列化)。
- 如果我們要把一棟房子從一個(gè)地方運(yùn)輸?shù)搅硪粋€(gè)地方去睹晒,序列化就是我把房子拆成一個(gè)個(gè)的磚塊放到車(chē)子里趟庄,然后留下一張房子原來(lái)結(jié)構(gòu)的圖紙,反序列化就是我們把房子運(yùn)輸?shù)搅四康牡匾院蟛嵴校鶕?jù)圖紙把一塊塊磚頭還原成房子原來(lái)面目的過(guò)程
什么情況下需要序列化
- 通過(guò)上面我想你已經(jīng)知道了凡是需要進(jìn)行“跨平臺(tái)存儲(chǔ)”和”網(wǎng)絡(luò)傳輸”的數(shù)據(jù)岔激,都需要進(jìn)行序列化。
- 本質(zhì)上存儲(chǔ)和網(wǎng)絡(luò)傳輸 都需要經(jīng)過(guò) 把一個(gè)對(duì)象狀態(tài)保存成一種跨平臺(tái)識(shí)別的字節(jié)格式是掰,然后其他的平臺(tái)才可以通過(guò)字節(jié)信息解析還原對(duì)象信息虑鼎。
序列化的方式
序列化只是一種拆裝組裝對(duì)象的規(guī)則,那么這種規(guī)則肯定也可能有多種多樣键痛,比如現(xiàn)在常見(jiàn)的序列化方式有:
JDK(不支持跨語(yǔ)言)炫彩、JSON、XML絮短、Hessian江兢、Kryo(不支持跨語(yǔ)言)、Thrift丁频、Protostuff杉允、FST(不支持跨語(yǔ)言)
Java 序列化
java 實(shí)現(xiàn)序列化很簡(jiǎn)單邑贴,只需要實(shí)現(xiàn)Serializable 接口即可。
public class User implements Serializable{
//年齡
private int age;
//名字
private String name ;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
注意: JAVA序列化中常見(jiàn)的問(wèn)題
問(wèn)題一:static 屬性不能被序列化
原因:序列化保存的是對(duì)象的狀態(tài)叔磷,靜態(tài)變量屬于類(lèi)的狀態(tài)拢驾,因此 序列化并不保存靜態(tài)變量。
問(wèn)題二:Transient 屬性不會(huì)被序列化
問(wèn)題三:序列化版本號(hào)serialVersionUID
所有實(shí)現(xiàn)序列化的對(duì)象都必須要有個(gè)版本號(hào)改基,這個(gè)版本號(hào)可以由我們自己定義繁疤,當(dāng)我們沒(méi)定義的時(shí)候JDK工具會(huì)按照我們對(duì)象的屬性生成一個(gè)對(duì)應(yīng)的版本號(hào)。使用JDK生成的serialVersionUID秕狰,只要對(duì)象有一丁點(diǎn)改變serialVersionUID就會(huì)隨著變更稠腊。因此建議自己手動(dòng)定義該版本號(hào)。
//////////////////////////////////////////////////////////////////////////////華麗的分割線///////////////////////////////////////////////////////////////////////////////////
redis序列化方式對(duì)比:
redis的默認(rèn)方式是JdkSerializationRedisSerializer
JdkSerializationRedisSerializer: 使用JDK提供的序列化功能鸣哀。 優(yōu)點(diǎn)是反序列化時(shí)不需要提供類(lèi)型信息(class)架忌,但缺點(diǎn)是需要實(shí)現(xiàn)Serializable接口,還有序列化后的結(jié)果非常龐大诺舔,是JSON格式的5倍左右鳖昌,這樣就會(huì)消耗redis服務(wù)器的大量?jī)?nèi)存。
Jackson2JsonRedisSerializer: 使用Jackson庫(kù)將對(duì)象序列化為JSON字符串低飒。優(yōu)點(diǎn)是速度快,序列化后的字符串短小精悍懂盐,不需要實(shí)現(xiàn)Serializable接口褥赊。但缺點(diǎn)也非常致命,那就是此類(lèi)的構(gòu)造函數(shù)中有一個(gè)類(lèi)型參數(shù)莉恼,必須提供要序列化對(duì)象的類(lèi)型信息(.class對(duì)象)拌喉。 通過(guò)查看源代碼,發(fā)現(xiàn)其只在反序列化過(guò)程中用到了類(lèi)型信息俐银。
問(wèn)題:使用默認(rèn)的JDK序列化方式尿背,在RDM工具中查看k-v值時(shí)會(huì)出現(xiàn)“亂碼”,不方便查看捶惜。
解決:自定義系列化方式田藐,使用Jackson2JsonRedisSerializer
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis配置
*
* @author LinJie
*/
@Configuration
public class RedisConfig {
/**
* Redis repository redis repository.
*
* @param redisTemplate the redis template
* @return the redis repository
*/
@Bean
public RedisRepository redisRepository(RedisTemplate redisTemplate) {
// 使用Jackson2JsonRedisSerialize 替換默認(rèn)序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return new RedisRepository(redisTemplate);
}
}