本文参加「新人创作礼」活动,一同敞开创作之路。

我的变强之路

为什么Redis默许序列化器处理之后的key会带有乱码?


主张翻开代码跟着看


1、直接从yml装备中进入redis装备文件:

为什么Redis默认序列化器处理之后的key会带有乱码?


2、看下哪些文件用到了RedisProperties

为什么Redis默认序列化器处理之后的key会带有乱码?
发现只要一个类引证到了


3、进到RedisAutoConfiguration类

为什么Redis默认序列化器处理之后的key会带有乱码?
发现也是个主动装备的类,并且内部包含一个主动装备的静态内部类RedisConfiguration
为什么Redis默认序列化器处理之后的key会带有乱码?
能够看到这儿界说了RedisTemplate的bean,并初始化了RedisTemplate的ConnectionFactory特点


4、进入RedisTemplate

为什么Redis默认序列化器处理之后的key会带有乱码?
发现RedisTemplate承继了RedisAccessor并完成了两个接口 先搜一下内部有没有静态代码块
为什么Redis默认序列化器处理之后的key会带有乱码?
发现是没有的,那么看看它的父类(Java是强承继的)


5、RedisTemplate父类:RedisAccessor

为什么Redis默认序列化器处理之后的key会带有乱码?
好,要害来了 RedisAccessor完成了一个接口:InitializingBean
为什么Redis默认序列化器处理之后的key会带有乱码?
接口中只界说了一个办法:afterPropertiesSet(); 这个办法会在所有bean特点赋值之后被BeanFactory调用(注释翻译,具体为什么,后续看完springboot主动装备和bean的生命周期相关源码之后再补充)


6、那么回到RedisTemplate的父类RedisAccessor看看它里边的afterPropertiesSet()办法里做了些什么

为什么Redis默认序列化器处理之后的key会带有乱码?
界说了一个断语,用来判别RedisConnectionFactory是否被界说了


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

为什么Redis默认序列化器处理之后的key会带有乱码?
从界说默许序列化器那行代码进到的办法 先看一下SerializingConverter是个什么:
为什么Redis默认序列化器处理之后的key会带有乱码?
Serializer是什么现已再了解不过了吧,那么看看结构办法中的DefaultSerializer又是什么:
为什么Redis默认序列化器处理之后的key会带有乱码?
DefaultSerializer完成了jdk的Serializer接口并完成了里边的serialize办法,这个当地便是key最后被处理的当地。


9、以上便是redis默许序列化器的进程,上面说到一个类加载器 由于和文章标题没有太大联系就不细说了,至于作用 已然有序列化肯定就有反序列化,那个类加载器便是用来反序列化的


10、redisTemplate.opsForValue().set()这个办法和了解吧,往redis里做刺进操作,看看里边是怎样对key做操作的,进到RedisTemplate完成的RedisOperations接口:

为什么Redis默认序列化器处理之后的key会带有乱码?
找到opsForValue()办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
回来了一个ValueOperations目标,进到这个目标找到它的set办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
持续看完成了set办法的类:
为什么Redis默认序列化器处理之后的key会带有乱码?
这儿看到只要一个DefaultValueOperations类完成了set办法,跟进去:
为什么Redis默认序列化器处理之后的key会带有乱码?
set办法的庐山真面目! 其他的不管,看下key是怎样被处理的,这儿创立一个ValueDeserializingRedisCallback目标的时分把key传了进去,跟进去看下key有没有被做什么处理
为什么Redis默认序列化器处理之后的key会带有乱码?

仅仅给大局key赋值,这个时分key是ValueDeserializingRedisCallback目标的一个特点了;注意这个ValueDeserializingRedisCallback类是一个笼统类,里边的inRedis()是一个笼统办法,doInredis被final润饰 那么inRedis办法是要被完成的,而doInredis办法是不允许被修正的;


11、回到set办法:

为什么Redis默认序列化器处理之后的key会带有乱码?
知道key被赋给ValueDeserializingRedisCallback没有被操作后,那便是execute办法里对ValueDeserializingRedisCallback这个目标进行了操作,跟进execute办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
一向跟下来发现终究回到了RedisTemplate中的execute()办法,这个时分key是在目标action内的,找一下办法内有哪些当地用到了action这个目标:
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
发现只要两个当地用到了,第一个当地是一个断语,用于判别action是否为null,即没有被创立,第二个是调用了action本身的doInRedis办法,还记得这个action是哪个类里边的办法吧,有点绕的话回到第10步看一下。这个action是ValueDeserializingRedisCallback的目标,那么进到ValueDeserializingRedisCallback里边看一下其中的doInRedis()办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
能够看到doInRedis()办法里边有一个rawKey()办法对key做了处理,跟进去:
为什么Redis默认序列化器处理之后的key会带有乱码?
先看一下keySerializer()是个什么:
为什么Redis默认序列化器处理之后的key会带有乱码?
回来一个RedisSerializer目标(Redis序列化器),持续跟进template.getKeySerializer();
为什么Redis默认序列化器处理之后的key会带有乱码?
回到了RedisTemplate中的getKeySerializer()办法,回来了keySerializer,第7步中现已说到。没有指定默许序列化器的情况下keySerializer和defaultSerializer是相同的;已然keySerializer()不回来null,那么终究rawKey()办法回来的便是keySerializer().serialize(key);


12、跟进serialize():

为什么Redis默认序列化器处理之后的key会带有乱码?
这个JdkSerializationRedisSerializer了解吗?是不是便是初始化RedisTemplate的时分创立的那个序列化器?不记得的话回到第3步看一下;


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);
        }
    }
}

执行:

为什么Redis默认序列化器处理之后的key会带有乱码?
现在来一步步调试:
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
为什么Redis默认序列化器处理之后的key会带有乱码?
呈现了呈现了,能够看到创立ByteArrayOutputStream流的时分还一切正常,可是到了构建ObjectOutputStream流的时分呈现了乱码! 这个时分能够下个初步定论了:==运用Redis默许序列化器处理key,key的序列化进程中ByteArrayOutputStream转ObjectOutputStream时会发生乱码==

那么接下来看一下为什么ByteArrayOutputStream转ObjectOutputStream时会发生乱码:

为什么Redis默认序列化器处理之后的key会带有乱码?
进来ObjectOutputStream的结构办法,发现传进来的ByteArrayOutputStream被转换成了BlockDataOutputStream,那么后续操作的便是BlockDataOutputStream了 即目标bout

为什么Redis默认序列化器处理之后的key会带有乱码?
经过debug发现直到 writeStreamHeader()办法bout中的byte数组buf中仍是都全部为0,进入writeStreamHeader()办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
这儿对bout做了两次writeShort()操作;STREAM_MAGIC和STREAM_VERSION是两个常量,
为什么Redis默认序列化器处理之后的key会带有乱码?
跟进writeShort()办法:
为什么Redis默认序列化器处理之后的key会带有乱码?
直到Bits.putShort这一步,buf中还全部都为0 再持续看一下Bits.putShort这个办法是个什么操作:
为什么Redis默认序列化器处理之后的key会带有乱码?

传进来的byte[]数组的第off+1位变成val的byte值,第off位变成val右移8位的byte值! 好的,持续调试看看是不是由于这个putShort把buf给修正了:

为什么Redis默认序列化器处理之后的key会带有乱码?
这便是为什么Redis默许序列化器处理key之后会有乱码的原因地点了。

附上抽丝剥茧之后默许序列化器的执行代码:

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[]中的几位字节没有被修正,那么也就不会发生乱码了;

最后,如有缺乏欢迎指正。