MyBatis框架提供了二级缓存接口,咱们只须要实现它再开启配置就能够应用了。
特地留神,咱们要解决缓存穿透、缓存穿透和缓存雪崩的问题,同时也要保障缓存性能。
具体实现阐明,间接看代码正文吧!

1、开启配置

SpringBoot配置

mybatis:  configuration:    cache-enabled: true

2、Redis配置以及服务接口

RedisConfig.java

package com.leven.mybatis.api.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;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.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;/** * Redis缓存配置 * @author Leven * @date 2019-09-07 */@Configurationpublic class RedisConfig {    /**     * 配置自定义redisTemplate     * @return redisTemplate     */    @Bean    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        RedisTemplate<String, Object> template = new RedisTemplate<>();        template.setConnectionFactory(redisConnectionFactory);        // 应用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        ObjectMapper mapper = new ObjectMapper();        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(mapper);        template.setKeySerializer(stringRedisSerializer);        template.setValueSerializer(jackson2JsonRedisSerializer);        template.setHashKeySerializer(stringRedisSerializer);        template.setHashValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;    }}

RedisService.java

package com.leven.mybatis.core.service;import java.util.*;import java.util.concurrent.TimeUnit;/** * redis根底服务接口 * @author Leven * @date 2019-09-07 */public interface RedisService {// =============================common============================    /**     * 指定缓存生效工夫     * @param key 键     * @param time 工夫(秒)     */    void expire(String key, long time);    /**     * 指定缓存生效工夫     * @param key 键     * @param expireAt 生效工夫点     * @return 处理结果     */    void expireAt(String key, Date expireAt);    /**     * 依据key 获取过期工夫     * @param key 键 不能为null     * @return 工夫(秒) 返回0代表为永恒无效     */    Long getExpire(String key);    /**     * 判断key是否存在     * @param key 键     * @return true 存在 false不存在     */    Boolean hasKey(String key);    /**     * 删除缓存     * @param key 能够传一个值 或多个     */    void delete(String... key);    /**     * 删除缓存     * @param keys 能够传一个值 或多个     */    void delete(Collection<String> keys);    // ============================String=============================    /**     * 一般缓存获取     * @param key 键     * @return 值     */    Object get(String key);    /**     * 一般缓存放入     * @param key 键     * @param value 值     */    void set(String key, Object value);    /**     * 一般缓存放入并设置工夫     * @param key 键     * @param value 值     * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期     */    void set(String key, Object value, long time);    /**     * 一般缓存放入并设置工夫     * @param key 键     * @param value 值     * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期     */    void set(String key, Object value, long time, TimeUnit timeUnit);    /**     * 递增     * @param key 键     * @param value 要减少几(大于0)     * @return 递增后后果     */    Long incr(String key, long value);    /**     * 递加     * @param key 键     * @param value 要缩小几(大于0)     * @return 递加后后果     */    Long decr(String key, long value);    // ================================Map=================================    /**     * HashGet     * @param key 键 不能为null     * @param item 项 不能为null     * @return 值     */    Object hashGet(String key, String item);    /**     * 获取hashKey对应的所有键值     * @param key 键     * @return 对应的多个键值     */    Map<Object, Object> hashEntries(String key);    /**     * HashSet     * @param key 键     * @param map 对应多个键值     */    void hashSet(String key, Map<String, Object> map);    /**     * HashSet 并设置工夫     * @param key 键     * @param map 对应多个键值     * @param time 工夫(秒)     */    void hashSet(String key, Map<String, Object> map, long time);    /**     * 向一张hash表中放入数据,如果不存在将创立     * @param key 键     * @param item 项     * @param value 值     */    void hashSet(String key, String item, Object value);    /**     * 向一张hash表中放入数据,如果不存在将创立     * @param key 键     * @param item 项     * @param value 值     * @param time 工夫(秒) 留神:如果已存在的hash表有工夫,这里将会替换原有的工夫     */    void hashSet(String key, String item, Object value, long time);    /**     * 删除hash表中的值     * @param key 键 不能为null     * @param item 项 能够使多个 不能为null     */    void hashDelete(String key, Object... item);    /**     * 删除hash表中的值     * @param key 键 不能为null     * @param items 项 能够使多个 不能为null     */    void hashDelete(String key, Collection items);    /**     * 判断hash表中是否有该项的值     * @param key 键 不能为null     * @param item 项 不能为null     * @return true 存在 false不存在     */    Boolean hashHasKey(String key, String item);    /**     * hash递增 如果不存在,就会创立一个 并把新增后的值返回     * @param key 键     * @param item 项     * @param value 要减少几(大于0)     * @return 递增后后果     */    Double hashIncr(String key, String item, double value);    /**     * hash递加     * @param key 键     * @param item 项     * @param value 要缩小记(小于0)     * @return 递加后后果     */    Double hashDecr(String key, String item, double value);    // ============================set=============================    /**     * 依据key获取Set中的所有值     * @param key 键     * @return set汇合     */    Set<Object> setGet(String key);    /**     * 依据value从一个set中查问,是否存在     * @param key 键     * @param value 值     * @return true 存在 false不存在     */    Boolean setIsMember(String key, Object value);    /**     * 将数据放入set缓存     * @param key 键     * @param values 值 能够是多个     * @return 胜利个数     */    Long setAdd(String key, Object... values);    /**     * 将数据放入set缓存     * @param key 键     * @param values 值 能够是多个     * @return 胜利个数     */    Long setAdd(String key, Collection values);    /**     * 将set数据放入缓存     * @param key 键     * @param time 工夫(秒)     * @param values 值 能够是多个     * @return 胜利个数     */    Long setAdd(String key, long time, Object... values);    /**     * 获取set缓存的长度     * @param key 键     * @return set长度     */    Long setSize(String key);    /**     * 移除值为value的     * @param key 键     * @param values 值 能够是多个     * @return 移除的个数     */    Long setRemove(String key, Object... values);    // ===============================list=================================    /**     * 获取list缓存的内容     * @param key 键     * @param start 开始     * @param end 完结 0 到 -1代表所有值     * @return 缓存列表     */    List<Object> listRange(String key, long start, long end);    /**     * 获取list缓存的长度     * @param key 键     * @return 长度     */    Long listSize(String key);    /**     * 通过索引 获取list中的值     * @param key 键     * @param index 索引 index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推     * @return 值     */    Object listIndex(String key, long index);    /**     * 将list放入缓存     * @param key 键     * @param value 值     */    void listRightPush(String key, Object value);    /**     * 将list放入缓存     * @param key 键     * @param value 值     * @param time 工夫(秒)     */    void listRightPush(String key, Object value, long time);    /**     * 将list放入缓存     * @param key 键     * @param value 值     */    void listRightPushAll(String key, List<Object> value);    /**     * 将list放入缓存     *     * @param key 键     * @param value 值     * @param time 工夫(秒)     */    void listRightPushAll(String key, List<Object> value, long time);    /**     * 依据索引批改list中的某条数据     * @param key 键     * @param index 索引     * @param value 值     */    void listSet(String key, long index, Object value);    /**     * 移除N个值为value     * @param key 键     * @param count 移除多少个     * @param value 值     * @return 移除的个数     */    Long listRemove(String key, long count, Object value);}

RedisServiceImpl.java

package com.leven.mybatis.core.service.impl;import com.leven.commons.model.exception.SPIException;import com.leven.mybatis.model.constant.Constant;import com.leven.mybatis.core.service.RedisService;import com.leven.mybatis.model.constant.ExceptionCode;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import java.util.*;import java.util.concurrent.TimeUnit;/** * redis根底服务接口实现 * @author Leven * @date 2019-09-07 */@Slf4j@Servicepublic class RedisServiceImpl implements RedisService {    /**     *      */    private static final String PREFIX = Constant.APPLICATION;    @Autowired    private RedisTemplate<String, Object> redisTemplate;    // =============================common============================    /**     * 指定缓存生效工夫     * @param key 键     * @param time 工夫(秒)     */    @Override    public void expire(String key, long time) {        redisTemplate.expire(getKey(key), time, TimeUnit.SECONDS);    }    /**     * 指定缓存生效工夫     * @param key 键     * @param expireAt 生效工夫点     * @return 处理结果     */    @Override    public void expireAt(String key, Date expireAt) {        redisTemplate.expireAt(getKey(key), expireAt);    }    /**     * 依据key 获取过期工夫     * @param key 键 不能为null     * @return 工夫(秒) 返回0代表为永恒无效     */    @Override    public Long getExpire(String key) {        return redisTemplate.getExpire(getKey(key), TimeUnit.SECONDS);    }    /**     * 判断key是否存在     * @param key 键     * @return true 存在 false不存在     */    @Override    public Boolean hasKey(String key) {        return redisTemplate.hasKey(getKey(key));    }    /**     * 删除缓存     * @param keys 能够传一个值 或多个     */    @Override    public void delete(String... keys) {        if (keys != null && keys.length > 0) {            if (keys.length == 1) {                redisTemplate.delete(getKey(keys[0]));            } else {                List<String> keyList = new ArrayList<>(keys.length);                for (String key : keys) {                    keyList.add(getKey(key));                }                redisTemplate.delete(keyList);            }        }    }    /**     * 删除缓存     * @param keys 能够传一个值 或多个     */    @Override    public void delete(Collection<String> keys) {        if (keys != null && !keys.isEmpty()) {            List<String> keyList = new ArrayList<>(keys.size());            for (String key : keys) {                keyList.add(getKey(key));            }            redisTemplate.delete(keyList);        }    }    // ============================String=============================    /**     * 一般缓存获取     * @param key 键     * @return 值     */    @Override    public Object get(String key) {        return key == null ? null : redisTemplate.opsForValue().get(getKey(key));    }    /**     * 一般缓存放入     * @param key 键     * @param value 值     */    @Override    public void set(String key, Object value) {        redisTemplate.opsForValue().set(getKey(key), value);    }    /**     * 一般缓存放入并设置工夫     * @param key 键     * @param value 值     * @param time 工夫(秒) time要大于0 如果time小于等于0 将设置无限期     */    @Override    public void set(String key, Object value, long time) {        set(key, value, time, TimeUnit.SECONDS);    }    /**     * 一般缓存放入并设置工夫     * @param key 键     * @param value 值     * @param time 工夫 time要大于0 如果time小于等于0 将设置无限期     * @param timeUnit 工夫单位     */    @Override    public void set(String key, Object value, long time, TimeUnit timeUnit) {        if (time > 0) {            redisTemplate.opsForValue().set(getKey(key), value, time, timeUnit);        } else {            set(getKey(key), value);        }    }    /**     * 递增     * @param key 键     * @param value 要减少几(大于0)     * @return 递增后后果     */    @Override    public Long incr(String key, long value) {        if (value < 1) {            throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递增因子必须大于0");        }        return redisTemplate.opsForValue().increment(getKey(key), value);    }    /**     * 递加     * @param key 键     * @param value 要缩小几(大于0)     * @return 递加后后果     */    @Override    public Long decr(String key, long value) {        if (value < 1) {            throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递加因子必须大于0");        }        return redisTemplate.opsForValue().decrement(getKey(key), value);    }    // ================================Map=================================    /**     * HashGet     * @param key 键 不能为null     * @param item 项 不能为null     * @return 值     */    @Override    public Object hashGet(String key, String item) {        return redisTemplate.opsForHash().get(getKey(key), item);    }    /**     * 获取hashKey对应的所有键值     * @param key 键     * @return 对应的多个键值     */    @Override    public Map<Object, Object> hashEntries(String key) {        return redisTemplate.opsForHash().entries(getKey(key));    }    /**     * HashSet     * @param key 键     * @param map 对应多个键值     */    @Override    public void hashSet(String key, Map<String, Object> map) {        redisTemplate.opsForHash().putAll(getKey(key), map);    }    /**     * HashSet 并设置工夫     * @param key 键     * @param map 对应多个键值     * @param time 工夫(秒)     */    @Override    public void hashSet(String key, Map<String, Object> map, long time) {        String k = getKey(key);        redisTemplate.opsForHash().putAll(k, map);        if (time > 0) {            expire(k, time);        }    }    /**     * 向一张hash表中放入数据,如果不存在将创立     * @param key 键     * @param item 项     * @param value 值     */    @Override    public void hashSet(String key, String item, Object value) {        redisTemplate.opsForHash().putIfAbsent(getKey(key), item, value);    }    /**     * 向一张hash表中放入数据,如果不存在将创立     * @param key 键     * @param item 项     * @param value 值     * @param time 工夫(秒) 留神:如果已存在的hash表有工夫,这里将会替换原有的工夫     */    @Override    public void hashSet(String key, String item, Object value, long time) {        String k = getKey(key);        redisTemplate.opsForHash().putIfAbsent(k, item, value);        if (time > 0) {            expire(k, time);        }    }    /**     * 删除hash表中的值     * @param key 键 不能为null     * @param item 项 能够使多个 不能为null     */    @Override    public void hashDelete(String key, Object... item) {        redisTemplate.opsForHash().delete(getKey(key), item);    }    /**     * 删除hash表中的值     * @param key 键 不能为null     * @param items 项 能够使多个 不能为null     */    @Override    public void hashDelete(String key, Collection items) {        redisTemplate.opsForHash().delete(getKey(key), items.toArray());    }    /**     * 判断hash表中是否有该项的值     * @param key 键 不能为null     * @param item 项 不能为null     * @return true 存在 false不存在     */    @Override    public Boolean hashHasKey(String key, String item) {        return redisTemplate.opsForHash().hasKey(getKey(key), item);    }    /**     * hash递增 如果不存在,就会创立一个 并把新增后的值返回     * @param key 键     * @param item 项     * @param value 要减少几(大于0)     * @return 递增后后果     */    @Override    public Double hashIncr(String key, String item, double value) {        if (value < 1) {            throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递增因子必须大于0");        }        return redisTemplate.opsForHash().increment(getKey(key), item, value);    }    /**     * hash递加     * @param key 键     * @param item 项     * @param value 要缩小记(小于0)     * @return 递加后后果     */    @Override    public Double hashDecr(String key, String item, double value) {        if (value < 1) {            throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"递加因子必须大于0");        }        return redisTemplate.opsForHash().increment(getKey(key), item, -value);    }    // ============================set=============================    /**     * 依据key获取Set中的所有值     * @param key 键     * @return set汇合     */    @Override    public Set<Object> setGet(String key) {        return redisTemplate.opsForSet().members(getKey(key));    }    /**     * 依据value从一个set中查问,是否存在     * @param key 键     * @param value 值     * @return true 存在 false不存在     */    @Override    public Boolean setIsMember(String key, Object value) {        return redisTemplate.opsForSet().isMember(getKey(key), value);    }    /**     * 将数据放入set缓存     * @param key 键     * @param values 值 能够是多个     * @return 胜利个数     */    @Override    public Long setAdd(String key, Object... values) {        return redisTemplate.opsForSet().add(getKey(key), values);    }    /**     * 将数据放入set缓存     * @param key 键     * @param values 值 能够是多个     * @return 胜利个数     */    @Override    public Long setAdd(String key, Collection values) {        return redisTemplate.opsForSet().add(getKey(key), values.toArray());    }    /**     * 将set数据放入缓存     * @param key 键     * @param time 工夫(秒)     * @param values 值 能够是多个     * @return 胜利个数     */    @Override    public Long setAdd(String key, long time, Object... values) {        String k = getKey(key);        Long count = redisTemplate.opsForSet().add(k, values);        if (time > 0){            expire(k, time);        }        return count;    }    /**     * 获取set缓存的长度     * @param key 键     * @return set长度     */    @Override    public Long setSize(String key) {        return redisTemplate.opsForSet().size(getKey(key));    }    /**     * 移除值为value的     * @param key 键     * @param values 值 能够是多个     * @return 移除的个数     */    @Override    public Long setRemove(String key, Object... values) {        return redisTemplate.opsForSet().remove(getKey(key), values);    }    // ===============================list=================================    /**     * 获取list缓存的内容     * @param key 键     * @param start 开始     * @param end 完结 0 到 -1代表所有值     * @return 缓存列表     */    @Override    public List<Object> listRange(String key, long start, long end) {        return redisTemplate.opsForList().range(getKey(key), start, end);    }    /**     * 获取list缓存的长度     * @param key 键     * @return 长度     */    @Override    public Long listSize(String key) {        return redisTemplate.opsForList().size(getKey(key));    }    /**     * 通过索引 获取list中的值     * @param key 键     * @param index 索引 index>=0时, 0 表头,1 第二个元素,顺次类推;index<0时,-1,表尾,-2倒数第二个元素,顺次类推     * @return 值     */    @Override    public Object listIndex(String key, long index) {        return redisTemplate.opsForList().index(getKey(key), index);    }    /**     * 将list放入缓存     * @param key 键     * @param value 值     */    @Override    public void listRightPush(String key, Object value) {        redisTemplate.opsForList().rightPush(getKey(key), value);    }    /**     * 将list放入缓存     * @param key 键     * @param value 值     * @param time 工夫(秒)     */    @Override    public void listRightPush(String key, Object value, long time) {        String k = getKey(key);        redisTemplate.opsForList().rightPush(k, value);        if (time > 0){            expire(k, time);        }    }    /**     * 将list放入缓存     * @param key 键     * @param value 值     */    @Override    public void listRightPushAll(String key, List<Object> value) {        redisTemplate.opsForList().rightPushAll(getKey(key), value);    }    /**     * 将list放入缓存     *     * @param key 键     * @param value 值     * @param time 工夫(秒)     */    @Override    public void listRightPushAll(String key, List<Object> value, long time) {        String k = getKey(key);        redisTemplate.opsForList().rightPushAll(k, value);        if (time > 0) {            expire(k, time);        }    }    /**     * 依据索引批改list中的某条数据     * @param key 键     * @param index 索引     * @param value 值     */    @Override    public void listSet(String key, long index, Object value) {        redisTemplate.opsForList().set(getKey(key), index, value);    }    /**     * 移除N个值为value     * @param key 键     * @param count 移除多少个     * @param value 值     * @return 移除的个数     */    @Override    public Long listRemove(String key, long count, Object value) {        return redisTemplate.opsForList().remove(getKey(key), count, value);    }    private String getKey(String key) {        return PREFIX + ":" + key;    }}

3、实现MyBatis的Cache接口

MybatisRedisCache.java

package com.leven.mybatis.core.cache;import com.leven.commons.core.util.ApplicationContextUtils;import com.leven.commons.model.exception.SPIException;import com.leven.mybatis.core.service.RedisService;import com.leven.mybatis.model.constant.ExceptionCode;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.RandomUtils;import org.apache.ibatis.cache.Cache;import java.security.MessageDigest;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * MyBatis二级缓存Redis实现 * 重点解决以下几个问题 * 1、缓存穿透:存储空值解决,MyBatis框架实现 * 2、缓存击穿:应用互斥锁,咱们本人实现 * 3、缓存雪崩:缓存有效期设置为一个随机范畴,咱们本人实现 * 4、读写性能:redis key不能过长,会影响性能,这里应用SHA-256计算摘要当成key * @author Leven * @date 2019-09-07 */@Slf4jpublic class MybatisRedisCache implements Cache {    /**     * 对立字符集     */    private static final String CHARSET = "utf-8";    /**     * key摘要算法     */    private static final String ALGORITHM = "SHA-256";    /**     * 对立缓存头     */    private static final String CACHE_NAME = "MyBatis:";    /**     * 读写锁:解决缓存击穿     */    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();    /**     * 表空间ID:不便前面的缓存清理     */    private final String id;    /**     * redis服务接口:提供根本的读写和清理     */    private static volatile RedisService redisService;    /**     * 信息摘要     */    private volatile MessageDigest messageDigest;    /////////////////////// 解决缓存雪崩,具体范畴依据业务须要设置正当值 //////////////////////////    /**     * 缓存最小有效期     */    private static final int MIN_EXPIRE_MINUTES = 60;    /**     * 缓存最大有效期     */    private static final int MAX_EXPIRE_MINUTES = 120;    /**     * MyBatis给每个表空间初始化的时候要用到     * @param id 其实就是namespace的值     */    public MybatisRedisCache(String id) {        if (id == null) {            throw new IllegalArgumentException("Cache instances require an ID");        }        this.id = id;    }    /**     * 获取ID     * @return 实在值     */    @Override    public String getId() {        return id;    }    /**     * 创立缓存     * @param key 其实就是sql语句     * @param value sql语句查问后果     */    @Override    public void putObject(Object key, Object value) {        try {            String strKey = getKey(key);            // 有效期为1~2小时之间随机,避免雪崩            int expireMinutes = RandomUtils.nextInt(MIN_EXPIRE_MINUTES, MAX_EXPIRE_MINUTES);            getRedisService().set(strKey, value, expireMinutes, TimeUnit.MINUTES);            log.debug("Put cache to redis, id={}", id);        } catch (Exception e) {            log.error("Redis put failed, id=" + id, e);        }    }    /**     * 读取缓存     * @param key 其实就是sql语句     * @return 缓存后果     */    @Override    public Object getObject(Object key) {        try {            String strKey = getKey(key);            log.debug("Get cache from redis, id={}", id);            return getRedisService().get(strKey);        } catch (Exception e) {            log.error("Redis get failed, fail over to db", e);            return null;        }    }    /**     * 删除缓存     * @param key 其实就是sql语句     * @return 后果     */    @Override    public Object removeObject(Object key) {        try {            String strKey = getKey(key);            getRedisService().delete(strKey);            log.debug("Remove cache from redis, id={}", id);        } catch (Exception e) {            log.error("Redis remove failed", e);        }        return null;    }    /**     * 缓存清理     * 网上好多博客这里用了flushDb甚至是flushAll,感觉好坑鸭!     * 应该是依据表空间进行清理     */    @Override    public void clear() {        try {            log.debug("clear cache, id={}", id);            String hsKey = CACHE_NAME + id;            // 获取CacheNamespace所有缓存key            Map<Object, Object> idMap = getRedisService().hashEntries(hsKey);            if (!idMap.isEmpty()) {                Set<Object> keySet = idMap.keySet();                Set<String> keys = new HashSet<>(keySet.size());                keySet.forEach(item -> keys.add(item.toString()));                // 清空CacheNamespace所有缓存                getRedisService().delete(keys);                // 清空CacheNamespace                getRedisService().delete(hsKey);            }        } catch (Exception e) {            log.error("clear cache failed", e);        }    }    /**     * 获取缓存大小,临时没用上     * @return 长度     */    @Override    public int getSize() {        return 0;    }    /**     * 获取读写锁:为了解决缓存击穿     * @return 锁     */    @Override    public ReadWriteLock getReadWriteLock() {        return readWriteLock;    }    /**     * 计算出key的摘要     * @param cacheKey CacheKey     * @return 字符串key     */    private String getKey(Object cacheKey) {        String cacheKeyStr = cacheKey.toString();        log.debug("count hash key, cache key origin string:{}", cacheKeyStr);        String strKey = byte2hex(getSHADigest(cacheKeyStr));        log.debug("hash key:{}", strKey);        String key = CACHE_NAME + strKey;        // 在redis额定保护CacheNamespace创立的key,clear的时候只清理以后CacheNamespace的数据        getRedisService().hashSet(CACHE_NAME + id, key, "1");        return key;    }    /**     * 获取信息摘要     * @param data 待计算字符串     * @return 字节数组     */    private byte[] getSHADigest(String data) {        try {            if (messageDigest == null) {                synchronized (MessageDigest.class) {                    if (messageDigest == null) {                        messageDigest = MessageDigest.getInstance(ALGORITHM);                    }                }            }            return messageDigest.digest(data.getBytes(CHARSET));        } catch (Exception e) {            log.error("SHA-256 digest error: ", e);            throw new SPIException(ExceptionCode.RUNTIME_UNITE_EXP,"SHA-256 digest error, id=" + id +  ".");        }    }    /**     * 字节数组转16进制字符串     * @param bytes 待转换数组     * @return 16进制字符串     */    private String byte2hex(byte[] bytes) {        StringBuilder sign = new StringBuilder();        for (byte aByte : bytes) {            String hex = Integer.toHexString(aByte & 0xFF);            if (hex.length() == 1) {                sign.append("0");            }            sign.append(hex.toUpperCase());        }        return sign.toString();    }    /**     * 获取Redis服务接口     * 应用双重查看保障线程平安     * @return 服务实例     */    private RedisService getRedisService() {        if (redisService == null) {            synchronized (RedisService.class) {                if (redisService == null) {                    redisService = ApplicationContextUtils.getBeanByClass(RedisService.class);                }            }        }        return redisService;    }}