场景一
在使用 increment 时发生报错
io.lettuce.core.RedisCommandExecutionException: ERR value is not an integer or out of range
如下:
int id = (Integer) redisTemplate.opsForValue().get("id"); redisTemplate.opsForValue().increment("id",5);
场景二
成功执行了如下代码
@Test void contextLoads() { redisTemplate.opsForValue().set("id",0); }
Redis中却没出现 id 的key,而出现了一串字符 ,就连 value 也不是 0
上面两种场景都是因为 RedisTemplate 的序列化
上面我们可以看到 redistTemplate 的 set 方法接收的是 Object对象
redisTemplate的功能就是接收任何类型的对象,然后底层通过JDK序列化器,将Java对象转化为Redis可以识别的字节
我们跟踪 redistTemplate 的 set方法,可以发现set底层调用了 DefaultValueOperations<K, V> 中的set方法如下:
public void set(K key, V value) { final byte[] rawValue = this.rawValue(value); this.execute(new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key) { protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }); }
我们发现,传入的value被 rawValue方法转换成 byte数组,继续跟踪 rawValue方法
byte[] rawValue(Object value) { return this.valueSerializer() == null && value instanceof byte[] ? (byte[])((byte[])value) : this.valueSerializer().serialize(value); }
我们发现调用了serialize方法,继续跟踪serialize方法,发现我们进到了JdkSerializationRedisSerializer 的 serialize 方法,如下:
public byte[] serialize(@Nullable Object object) { if (object == null) { return SerializationUtils.EMPTY_ARRAY; } else { try { return (byte[])this.serializer.convert(object); } catch (Exception var3) { throw new SerializationException("Cannot serialize", var3); } } }
不断深入跟踪上面的convert方法,最后来到了 DefaultSerializer 中的 serialize方法,我们看到底层用到了 ObjectOutputStream 将object对象转换为字节流写入Redis, 从而就达到了我们看到的效果
public void serialize(Object object, OutputStream outputStream) throws IOException { if (!(object instanceof Serializable)) { throw new IllegalArgumentException(this.getClass().getSimpleName() + " requires a Serializable payload but received an object of type [" + object.getClass().getName() + "]"); } else { ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(object); objectOutputStream.flush(); } }
这种序列化方式有俩个弊端:
- 可读性极差
- 内存占用大
如何做到 写入 的跟 存入 的是一样的呢?
这只需要修改RedisTemplate的序列化方式,添加如下配置类
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){ // 创建RedisTemplate对象 RedisTemplate<String, Object> template = new RedisTemplate<>(); // 设置连接工厂 template.setConnectionFactory(connectionFactory); // 创建JSON序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // 设置Key的序列化 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // 设置Value的序列化 template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); // 返回 return template; } }
重新运行
查看Redis
成功了,没毛病 ^ - ^
插入一个对象看看
import lombok.Data; @Data public class User { private String name; private Integer age; }
还是没毛病 嘿嘿 ^ - ^