MyBatis整合Redis实现二级缓存的示例代码

网友投稿 255 2022-11-26


MyBatis整合Redis实现二级缓存的示例代码

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

*/

@Configuration

public class RedisConfig {

/**

* 配置自定义redisTemplate

* @return redisTemplate

*/

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {

RedisTemplate template = new RedisTemplate<>();

template.setConnectionFactory(redisConnectionFactory);

// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值

Jackson2JsonRedisSerializer 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 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 hashEntries(String key);

/**

* HashSet

* @param key 键

* @param map 对应多个键值

*/

void hashSet(String key, Map map);

/**

* HashSet 并设置时间

* @param key 键

* @param map 对应多个键值

* @param time 时间(秒)

*/

void hashSet(String key, Map 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 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 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 value);

/**

* 将list放入缓存

*

* @param key 键

* @param value 值

* @param time 时间(秒)

*/

void listRightPushAll(String key, List 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

@Service

public class RedisServiceImpl implements RedisService {

/**

*

*/

private static final String PREFIX = Constant.APPLICATION;

@Autowired

private RedisTemplate 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 可以传一个值 或多个

*/

@http://Override

public void delete(String... keys) {

if (keys != null && keys.length > 0) {

if (keys.length == 1) {

redisTemplate.delete(getKey(keys[0]));

} else {

List keyList = new ArrayList<>(keys.length);

for (String key : keys) {

keyList.add(getKey(key));

}

redisTemplate.delete(keyList);

}

}

}

/**

* 删除缓存

* @param keys 可以传一个值 或多个

*/

@Override

public void delete(Collection keys) {

if (keys != null && !keys.isEmpty()) {

List 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 hashEntries(String key) {

return redisTemplate.opsForHash().entries(getKey(key));

}

/**

* HashSet

* @param key 键

* @param map 对应多个键值

*/

@Override

public void hashSet(String key, Map map) {

redisTemplate.opsForHash().putAll(getKey(key), map);

}

/**

* HashSet 并设置时间

* @param key 键

* @param map 对应多个键值

* @param time 时间(秒)

*/

@Override

public void hashSet(String key, Map map, long time) {

StrhJceIRling 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 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 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 value) {

redisTemplate.opsForList().rightPushAll(getKey(key), value);

}

/**

* 将list放入缓存

*

* @param key 键

* @param value 值

* @param time 时间(秒)

*/

@Override

public void listRightPushAll(String key, List 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

*/

@Slf4j

public 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 idMap = getRedisService().hashEntries(hsKey);

if (!idMap.isEmpty()) {

Set keySet = idMap.keySet();

Set 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;

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java项目打包成可执行jar用log4j将日志写在jar所在目录操作
下一篇:java启动jar包将日志打印到文本的简单操作
相关文章

 发表评论

暂时没有评论,来抢沙发吧~