解决方案
既然你需要GenericJackson2JsonRedisSerializer它的通用性,那么你就得接受他只能处理Object类型。
因此在使用的时候遇上这种情况,需要稍加注意了。我们可以先用Object接收,然后转成字符串再调用Long.valueOf()方法去间接实现。。。或者你在使用前手动指定序列化类型,但十分、十分不建议这么去做
它处理List、Set、Long类型等都会有类似的问题。使用的时候稍加注意即可(因为Java中默认数字类型是Integer、Double等)
当然还有一种方案是自定义序列化器:如自定义String序列化器,接受一切类型(官方的泛型限制了只接受String类型。这么一来,@Cacheable等注解的key支持不仅仅是String类型了):
/** * 必须重写序列化器,否则@Cacheable注解的key会报类型转换错误 */ 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) { //底层还是调用的fastjson的工具来操作的 String string = JSON.toJSONString(object); if (string == null) { return null; } string = string.replace(target, replacement); return string.getBytes(charset); } }
顺便提一句:单元测试的时候可能碰上这个异常:WRONGTYPE Operation against a key holding the wrong kind of value,不要慌。这个是因为key的类型不一致导致,一般只有在测试情况下才会发生**。比如之前这个key用用作k-v的形式,现在把这个key当作set数据类型来用,就会报这个错,换给key就行。**
说明:Jackson2JsonRedisSerializer的效率稍微优于GenericJackson2JsonRedisSerializer,但是使用起来远没有Generic方便。
各位看官可以根据自己业务的实际情况,酌情选择吧~~~~
第三方序列化器:FastJsonRedisSerializer、KryoRedisSerializer
由于Redis的流行,很多第三方组件都提供了对应的序列化器。比较著名的有阿里巴巴的FastJsonRedisSerializer
还好ali默认已经帮我们实现了基于fastjson的序列化方式,我们都不用自己动手了。
FastJsonRedisSerializer和GenericFastJsonRedisSerializer
和上面一样讲述的一样,FastJsonRedisSerializer需要指定反序列化类型,而GenericFastJsonRedisSerializer则比较通用。但同样的Generic系列存在上面我说的同样的问题,大家使用时需要多加注意。
KryoRedisSerializer:它就没有这么这么友好了,但自己实现一个也是轻而易举的事:
public class KryoRedisSerializer<T> implements RedisSerializer<T> { private Kryo kryo = new Kryo(); @Override public byte[] serialize(T t) throws SerializationException { System.out.println("[serialize]" + t); byte[] buffer = new byte[2048]; Output output = new Output(buffer); kryo.writeClassAndObject(output, t); return output.toBytes(); } @Override public T deserialize(byte[] bytes) throws SerializationException { System.out.println("[deserialize]" + Arrays.toString(bytes)); Input input = new Input(bytes); @SuppressWarnings("unchecked") T t = (T) kryo.readClassAndObject(input); return t; } }
指定RedisTemplate的序列化方式
这个就比较简单了,可以在注册Bean的时候就set(推荐),也可以使用的时候再做(非常非常不推荐,会有并发安全问题)
@Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // 使用Jackson2JsonRedisSerialize 替换默认序列化(备注,此处我用Object为例,各位看官请换成自己的类型哦~) 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); // 设置value的序列化规则和 key的序列化规则 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setKeySerializer(new StringRedisSerializer()); // 最好是调用一下这个方法 redisTemplate.afterPropertiesSet(); return redisTemplate; }
java源生序列化的效率已经非常高了,但是kryo是java原生序列化性能十几倍(kryo只针对java语言,不跨语言。跨语言的序列化方式有:Protostuff、Thrift等。 所以如果你想自定义序列化器的话,个人建议可以导入kryo包,然后自己书写一个序列化器注册进去~~~)