RedisTemplate 结合 protostuff 做序列化存储

2,539 阅读4分钟

正常来说,redisTemplate是无法操作对象的,但是我们可以通过对其做序列化和反序列化作到对象的存储对象

不废话直接上代码

1,pom引用

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.7.2</version>
        </dependency>
        

2,对protostuff封装

/**
 * Description: protostuff 序列化工具.
 */
public class ProtostuffSerializeUtil {

    private static Logger logger = LoggerFactory.getLogger(ProtostuffSerializeUtil.class);

    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

    private static <T> Schema<T> getSchema(Class<T> clazz) {
        @SuppressWarnings("unchecked")
        Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.getSchema(clazz);
            if (schema != null) {
                cachedSchema.put(clazz, schema);
            }
        }
        return schema;
    }

    /**
     * 序列化.
     *
     * @param obj 需要序列化的obj
     */
    public static <T> byte[] serialize(T obj) {
        if (obj == null) {
            return null;
        }
        @SuppressWarnings("unchecked")
        Class<T> clazz = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(clazz);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            //logger.error(e.getMessage(), e);
            return null;
        } finally {
            buffer.clear();
        }
    }

    /**
     * 反序列化.
     *
     * @param data  需要反序列化的数据
     * @param clazz 结果class
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null || data.length < 1) {
            logger.error("ProtostuffSerializeUtil.deserialize --> 反序列化对象,byte序列为空");
            return null;
        }
        try {
            T obj = clazz.newInstance();
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(data, obj, schema);
            return obj;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 原生protobuf序列化.
     *
     * @param obj 需要序列化的obj
     */
    public static <T> byte[] serializeProtoBuf(T obj) {
        if (obj == null) {
            logger.error("ProtostuffSerializeUtil.serializeProtoBuf --> 序列化对象为空!");
            return null;
        }
        @SuppressWarnings("unchecked")
        Class<T> clazz = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(clazz);
            return ProtobufIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        } finally {
            buffer.clear();
        }
    }

    /**
     * 原生protobuf反序列化.
     *
     * @param data  需要反序列化的数据
     * @param clazz 结果class
     */
    public static <T> T deserializeProtoBuf(byte[] data, Class<T> clazz) {
        if (data == null || data.length < 1) {
            logger.error("ProtostuffSerializeUtil.deserializeProtoBuf --> 反序列化对象,byte序列为空");
            return null;
        }
        try {
            T obj = clazz.newInstance();
            Schema<T> schema = getSchema(clazz);
            ProtobufIOUtil.mergeFrom(data, obj, schema);
            return obj;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 序列化map.
     *
     * @param objMap 需要序列化的map
     * @return byte[]
     */
    public static byte[] serializeMap(Map<Object, Object> objMap) {
        if (objMap == null || objMap.isEmpty()) {
            logger.error("ProtostuffSerializeUtil.serializeMap --> 序列化map为空");
            return null;
        }
        MapWrapper wrapper = new MapWrapper(objMap);
        return serialize(wrapper);
    }

    /**
     * 反序列化map.
     *
     * @param data 需要反序列化的数据
     * @return map
     */
    public static Map<Object, Object> deserializeMap(byte[] data) {
        if (data == null || data.length < 1) {
            logger.error("ProtostuffSerializeUtil.deserialize --> 反序列化map,byte序列为空");
            return null;
        }
        MapWrapper wrapper = deserialize(data, MapWrapper.class);
        if (wrapper != null) {
            return wrapper.getMap();
        }
        return null;
    }

    /**
     * 序列化列表.
     *
     * @param objList 需要序列化的列表
     * @return 返回结果
     */
    public static <T> byte[] serializeList(List<T> objList) {
        if (objList == null || objList.isEmpty()) {
            logger.error("ProtostuffSerializeUtil.serializeList --> 序列化列表为空");
            return null;
        }
        @SuppressWarnings("unchecked")
        Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
        LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
            return bos.toByteArray();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        } finally {
            buffer.clear();
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 反序列化列表.
     *
     * @param data        需要反序列化的数据
     * @param targetClass Class
     * @return targetClass的列表
     */
    public static <T> List<T> deserializeList(byte[] data, Class<T> targetClass) {
        if (data == null || data.length == 0) {
            logger.error("ProtostuffSerializeUtil.deserializeList --> 序列化列表,byte序列为空");
            return null;
        }

        Schema<T> schema = RuntimeSchema.getSchema(targetClass);
        try {
            return ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(data), schema);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            return null;
        }
    }

}

3,结合RedisTemplate做封装

@Service("cacheService")
public class RedisCacheServiceImpl extends BaseLogger implements CacheService {

    @Resource
    private RedisTemplate<String, byte[]> redisTemplate;

    @Override
    public <V> void put(String key, V value) {

        doPut(key, serialize(value), null);
    }

    @Override
    public <V> void put(String key, V value, long expireTime) {
        doPut(key, serialize(value), expireTime);
    }

    @Override
    public <V> void putList(String key, List<V> value) {
        doPut(key, ProtostuffSerializeUtil.serializeList(value), null);
    }

    private void doPut(final String key, final byte[] value, final Long expireSec) {
        final byte[] keyBytes = serializeString(key);
        redisTemplate.execute(new RedisCallback<Object>() {
            public String doInRedis(final RedisConnection connection)
                    throws DataAccessException {
                connection.set(keyBytes, value);
                if (expireSec != null) {
                    connection.expire(keyBytes, expireSec);
                }
                return null;
            }
        });

    }

    @Override
    public void del(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public <V> V get(String key, Class<V> clazz) {
        return deserialize(doGet(key), clazz);
    }

    @Override
    public <V> List<V> getList(String key, Class<V> clazz) {
        return ProtostuffSerializeUtil.deserializeList(doGet(key), clazz);
    }

    private byte[] doGet(final String key) {
        return redisTemplate.execute(new RedisCallback<byte[]>() {
            public byte[] doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.get(serializeString(key));
            }
        });

    }


    @Override
    public <V> void hPut(String key, String hKey, V value) {
        final byte[] keyBytes = serializeString(key);
        final byte[] hKeyBytes = serializeString(hKey);
        final byte[] valBytes = serialize(value);
        redisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.hSet(keyBytes, hKeyBytes, valBytes);
            }
        });
    }

    @Override
    public void hDel(String key, String hKey) {
        redisTemplate.opsForHash().delete(key, hKey);
    }

    @Override
    public <V> V hGet(String key, String hKey, final Class<V> clazz) {
        final byte[] keyBytes = serializeString(key);
        final byte[] hkeyBytes = serializeString(hKey);
        return redisTemplate.execute(new RedisCallback<V>() {
            public V doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] data = connection.hGet(keyBytes, hkeyBytes);
                return deserialize(data, clazz);

            }
        });
    }


    @Override
    public <V> Map<String, V> hGetAll(String key, Class<V> clazz) {
        final byte[] keyBytes = serializeString(key);

        Map<byte[], byte[]> entries = redisTemplate.execute(new RedisCallback<Map<byte[], byte[]>>() {

            public Map<byte[], byte[]> doInRedis(RedisConnection connection) {
                return connection.hGetAll(keyBytes);
            }
        }, true);
        return deserializeMap(entries, clazz);
    }

    @Override
    public <V> List<V> hGetValues(String key, final Class<V> clazz) {
        final byte[] keyBytes = serializeString(key);
        return redisTemplate.execute(new RedisCallback<List<V>>() {

            public List<V> doInRedis(RedisConnection connection) {
                List<byte[]> valBytes = connection.hVals(keyBytes);
                List<V> val = new ArrayList<>();
                for (byte[] valByte : valBytes) {
                    val.add(ProtostuffSerializeUtil.deserialize(valByte, clazz));
                }
                return val;
            }
        }, true);
    }

    private <V> Map<String, V> deserializeMap(Map<byte[], byte[]> entries, Class<V> clazz) {
        if (entries == null) {
            return null;
        }

        Map<String, V> map = new LinkedHashMap<>(entries.size());

        for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
            map.put(entry.getKey() == null ? null : new String(entry.getKey()), deserialize(entry.getValue(), clazz));
        }
        return map;
    }

    @Override
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    private byte[] serializeString(String value) {
        return value == null ? null : value.getBytes(Charset.forName("UTF8"));
    }

    @SuppressWarnings("unchecked")
    private <V> byte[] serialize(V value) {
        if (value instanceof Map) {
            return ProtostuffSerializeUtil.serializeMap((Map<Object, Object>) value);
        } else {
            return ProtostuffSerializeUtil.serialize(value);
        }
    }

    @SuppressWarnings("unchecked")
    private <V> V deserialize(byte[] data, Class<V> clazz) {
        if (clazz.getName().equals(Map.class.getName())) {
            return (V) ProtostuffSerializeUtil.deserializeMap(data);
        } else {
            return ProtostuffSerializeUtil.deserialize(data, clazz);
        }
    }

}

完结,撒花