为什么Redis默认序列化器处理之后的key会带有乱码?
本文参加「新人创作礼」活动,一同敞开创作之路。
我的变强之路
为什么Redis默许序列化器处理之后的key会带有乱码?
主张翻开代码跟着看
1、直接从yml装备中进入redis装备文件:

2、看下哪些文件用到了RedisProperties
3、进到RedisAutoConfiguration类
4、进入RedisTemplate
5、RedisTemplate父类:RedisAccessor
6、那么回到RedisTemplate的父类RedisAccessor看看它里边的afterPropertiesSet()办法里做了些什么
7、再回到RedisTemplate看看它里边的afterPropertiesSet()办法里做了些什么
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
private RedisSerializer<?> defaultSerializer;
private ClassLoader classLoader;
/**以下几种序列化器都被界说为null*/
private RedisSerializer keySerializer = null;
private RedisSerializer valueSerializer = null;
private RedisSerializer hashKeySerializer = null;
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
private ScriptExecutor<K> scriptExecutor;
// cache singleton objects (where possible)
private ValueOperations<K, V> valueOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;
private GeoOperations<K, V> geoOps;
private HyperLogLogOperations<K, V> hllOps;
/**
* Constructs a new <code>RedisTemplate</code> instance.
*/
public RedisTemplate() {}
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
/**这儿便是默许序列化器的界说了*/
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
/**enableDefaultSerializer默许为true*/
if (enableDefaultSerializer) {
/**
*对keySerializer 、
* valueSerializer、
* hashKeySerializer、
* hashValueSerializer 赋值
*/
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}
==从代码中能够看到如果没有修正redis默许序列化器,那么redis的默许序列化器便是用当时类加载器为参数界说的一个JdkSerializationRedisSerializer目标,并且其中的keySerializer、valueSerializer、hashKeySerializer、hashValueSerializer四种序列化器都默许和defaultSerializer相同!==
8、接着进到JdkSerializationRedisSerializer
9、以上便是redis默许序列化器的进程,上面说到一个类加载器 由于和文章标题没有太大联系就不细说了,至于作用 已然有序列化肯定就有反序列化,那个类加载器便是用来反序列化的
10、redisTemplate.opsForValue().set()这个办法和了解吧,往redis里做刺进操作,看看里边是怎样对key做操作的,进到RedisTemplate完成的RedisOperations接口:
仅仅给大局key赋值,这个时分key是ValueDeserializingRedisCallback目标的一个特点了;注意这个ValueDeserializingRedisCallback类是一个笼统类,里边的inRedis()是一个笼统办法,doInredis被final润饰 那么inRedis办法是要被完成的,而doInredis办法是不允许被修正的;
11、回到set办法:
12、跟进serialize():
13、看到这儿如同还没有阐明为什么默许序列化器处理的key为什么会带有乱码;快了,以上梳理出了key的序列化进程和默许的序列化器,那么就写个程序来调用默许序列化器处理key,看看究竟哪一步搞出了乱码:
public class RedisKeySerialize<K,V> {
static {
System.out.println("=============REDIS序列化KEY东西==================");
}
public String scan(){
System.out.println("\n"+"* 请输入Key:");
return new Scanner(System.in).next();
}
/**处理key*/
public String handleKey(K key){
String returnStr = "";
final byte[] rawKey = rawKey(key);
returnStr = new String(rawKey);
return returnStr;
}
/**结构一个序列化器*/
RedisSerializer keySerializer() {
return new JdkSerializationRedisSerializer(this.getClass().getClassLoader());
}
/**序列化key*/
public byte[] rawKey(K key){
if (keySerializer() == null && key instanceof byte[]) {
return (byte[]) key;
}
return keySerializer().serialize(key);
}
public static void main(String[] args) {
RedisKeySerialize keySerialize = new RedisKeySerialize();
while (true) {
String key = keySerialize.scan();
String result = keySerialize.handleKey(key);
System.out.println("* 存于Redis的key为:");
System.out.println(result);
}
}
}
执行:
那么接下来看一下为什么ByteArrayOutputStream转ObjectOutputStream时会发生乱码:
传进来的byte[]数组的第off+1位变成val的byte值,第off位变成val右移8位的byte值!
好的,持续调试看看是不是由于这个putShort把buf给修正了:
附上抽丝剥茧之后默许序列化器的执行代码:
String key = "test";
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(key);
objectOutputStream.flush();
byte[] bytes = out.toByteArray();
String finalKey = new String(bytes);
总结:
1、redis默许序列化器:JdkSerializationRedisSerializer;
2、redis默许序列化器底层运用ByteArrayOutputStream流对key进行序列化操作;
3、序列化key的进程中ByteArrayOutputStream转为ObjectOutputStream;
4、ObjectOutputStream结构办法中将传入的输出流做了一个writeStreamHeader()操作,writeStreamHeader()调用Bits.putShort办法修正了流中本来为空的byte数组中的几位字节,导致本来为空的流有值;
就比如一个本来洁净的瓶子装的水倒出来仍是洁净的水,,但一个瓶子里本来有杂质,那么倒出来的水自然也就有杂质了。如果byte[]中的几位字节没有被修正,那么也就不会发生乱码了;
最后,如有缺乏欢迎指正。