spring-caching动态适配redis jsonarray和jsonobject

spring-caching动态适配redis jsonarray和jsonobject

  • 【原创】转载请注明出处
    • 背景
    • 原因分析
    • 解决思路
    • 解决方案

【原创】转载请注明出处

背景

  1. spring @EnableCaching框架的强大不再多说,想必大家都很清楚了;

  2. redis作为缓存服务器的首选也是众望所归

  3. 笔者的使用场景是缓存两种数据:
    a.以用户id为key,缓存用户账号相关数据
    b.以‘allNetworkAnchors’为key,缓存所有的主播账号数据

  4. 然后就开始愉快的编码了,

    @Cacheable(value = RedisCachingConfig.CACHE_USER, key = "'allNetworkAnchors'")
    public List<UserDTO> findAllNetworkAnchors() {
        log.info("findAllNetworkAnchors from db ");
        List<UserEntity> list = userService.list();
        return UserConvertor.INSTANCE.toDTOs(list);
    }

    @Cacheable(value = RedisCachingConfig.CACHE_USER, key = "#id", unless = "#result == null")
    public UserDTO findUserById(Long id) {
        log.info("query from db ,id: {}", id);
        UserEntity en = userService.getById(id);
        return UserConvertor.INSTANCE.toDTO(en);
    }
  1. 当前使用的RedisSerializer是:FastJsonRedisSerializer, 第一次调用服务,很自然的查询了数据库。
2020-05-20 15:16:33.301 [http-nio-18080-exec-4] INFO  xx.xx.UserService - findAllNetworkAnchors from db 
2020-05-20 15:19:14.388 [http-nio-18080-exec-8] INFO  xx.xx.UserService - query from db ,id: 1

第二次调用findUserById,也没有问题,很自然的走了redis缓存,当调用findAllNetworkAnchors时,错误出现了

org.springframework.data.redis.serializer.SerializationException: Could not deserialize: syntax error, expect {, actual [, pos 0, fastjson-version 1.2.51; nested exception is com.alibaba.fastjson.JSONException: syntax error, expect {, actual [, pos 0, fastjson-version 1.2.51
	at com.alibaba.fastjson.support.spring.FastJsonRedisSerializer.deserialize(FastJsonRedisSerializer.java:49)
	at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
	at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:226)

原因分析

从错误日志可以很明显的看出出错的原因:
redis中存储的是array格式的json,而反序列化时使用FastJsonRedisSerializer导致强转失败。

public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            return (T) JSON.parseObject(bytes, type, fastJsonConfig.getFeatures());
        } catch (Exception ex) {
            throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
        }
    }

解决思路

  1. 既然redis中存储的是array数据格式,我们使用JSON.parseArray方法是不是就ok了呢?
  2. 看下 RedisSerializer接口定义
public interface RedisSerializer<T> {

	byte[] serialize(@Nullable T t) throws SerializationException;

	@Nullable
	T deserialize(@Nullable byte[] bytes) throws SerializationException;
	....
}

可以看到上层接口已经限定了T,没有List的操作入口
3. 那我们在实现时,是否可以变通下,让deserialize返回的结果根据redis缓存的内容,来决定是array还是object呢?

解决方案

直接上代码, 使用自定义的FastJsonArrayCompatibleRedisSerializer作为redis的序列化执行器,问题解决!

public static class FastJsonArrayCompatibleRedisSerializer implements RedisSerializer<Object> {
        private static byte ARRAY_START_BYTE = '[';
        private FastJsonConfig fastJsonConfig = new FastJsonConfig();
        private Class<?> type;

        public FastJsonArrayCompatibleRedisSerializer(Class<?> type) {
            this.type = type;
        }

        public FastJsonConfig getFastJsonConfig() {
            return fastJsonConfig;
        }

        public void setFastJsonConfig(FastJsonConfig fastJsonConfig) {
            this.fastJsonConfig = fastJsonConfig;
        }

        @Override
        public byte[] serialize(Object t) throws SerializationException {
            if (t == null) {
                return new byte[0];
            }
            try {
                return JSON.toJSONBytes(t, fastJsonConfig.getSerializeConfig(), fastJsonConfig.getSerializerFeatures());
            } catch (Exception ex) {
                throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
            }
        }

        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            try {
                if (isArray(bytes[0])) {
                    return JSON.parseArray(new String(bytes), type);
                }

                return JSON.parseObject(bytes, type, fastJsonConfig.getFeatures());
            } catch (Exception ex) {
                throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
            }
        }

        /**
         * 判断第一个字符是否是‘[’
         * @param firstByte
         * @return
         */
        private boolean isArray(int firstByte) {
            return ARRAY_START_BYTE == firstByte;
        }
    }

你可能感兴趣的:(java,spring)