最近在用Spring注解方式Cacheable
整合Redis的时候,报ClassCastException
异常,我方法返回值是一个PageVo
, 报不能将PageVo
不能转换成String
类型。一开始网上搜都是说SpringBoot整合方案,但我用的是Spring的方式的,后来看过几篇文章,这里记录一下
一、为什么Spring redis中缓存的对象需要实现 Serializable 序列化接口
查看RedisTemplate源码,我们可以看到,在RedisTemplate中针对不同类型的数据提供了不同的序列化方式。
默认的序列化方式为JdkSerializationRedisSerializer
。而我们常用的配置为键采用StringRedisSerializer
来序列化,value采用默认的JdkSerializationSerializer
。
在JdkSerializationSerializer
的源码。可以看到序列化的时候调用了serializer.convert
方法。在convert方法中 调用的是 DefaultSerializer 下的serialize
方法。DefaultSerializer 下的serialize方法对Object对象的序列化方式是使ObjectOutputStream 将对象写入到outputStream中的。可以看到只有支持 java.io.Serializable 序列化接口的对象才能使用ObjectOutputStream
进行写入与读取。这就是为什么我们使用redis缓存对象时候需要让对象实现java.io.Serializable 序列化接口的原因。
二、定制我们的序列化工具
我是使用StringRedisSerializer做Value的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,在使用@Cacheable注解是Value属性就只能传String进来。,所以我返回值PageVo
不是String
类型,不能序列化为String
类型放入Redis的Value中。解决方案:把这个序列化方式重写了,将泛型改成Object
/**
* 必须重写序列化器,否则@Cacheable注解的value会报类型转换错误
*/
public class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
private final String target = "\"";
private final String replacement = "";
public StringRedisSerializer() {
this(Charset.forName("UTF8"));
}
public StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (string == null) {
return null;
}
string = string.replace(target, replacement);
return string.getBytes(charset);
}
}
三、自定义序列化的使用
最后如果要在Spring中使用这个序列化方法我们还需要咋redis的配置文件中注册一下
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<!-- 键序列化方式 -->
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<!-- 值序列化方式 -->
<property name="valueSerializer">
<!-- <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> -->
<!-- <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> -->
<bean class="com.yz.StringRedisSerializer "/>
</property>
</bean>