因为原生的@Cacheable 注解无法声明缓存时间,所以在这里可以自定义一个缓存注解。在声明缓存注解的过程中使用了redis,因为在上一篇文章中已经说明了Spring Boot如何整合redis组件了,所以在此就不做过多的说明了。
2.步骤 1.声明@RedisCache注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
//缓存名称
String value() default "";
//缓存的键名
String[] key() default {};
//过期时间,默认1小时,单位秒
int expiredTime() default 3600;
}
2.针对@RedisCache注解进行说明和预处理
@Component
@Aspect
public class RedisCacheAspect {
@Resource
private RedisUtil redisUtil;
@Resource
private RedissonClient redissonClient;
@Around("@annotation(com.fwy.common.annotation.redis.RedisCache)")
public Object cacheAroundAdvice(ProceedingJoinPoint point) throws Throwable {
// 获得连接点参数
Object[] args = point.getArgs();
// 声明一个对象Object,为方法的返回结果
Object result = null;
// 通过反射获得原始方法信息
MethodSignature signature = (MethodSignature)point.getSignature();
Type returnType = signature.getMethod().getGenericReturnType();
RedisCache redisCache = signature.getMethod().getAnnotation(RedisCache.class);
//获取缓存名称
String key = redisCache.value();
//获取缓存key
String[] keys = redisCache.key();
//获取缓存时间单位s
int expireTime = redisCache.expiredTime();
// 根据注解拼接缓存key
if (keys.length != 0) {
key += ":" + StringUtils.join(keys,":");
}
if(args.length != 0){
key += ":" + StringUtils.join(args,":");
}
// 缓存代码
result = cacheHit(returnType, key);
// 表示缓存不为空,则直接返回数据
if (result != null){
return result;
}
// 使用redisson获得分布式锁
RLock lock = redissonClient.getLock(key + ":lock");
Properties prop = new Properties();
try {
// 成功拿到分布式锁的,可以查询db
boolean b = lock.tryLock(100, 10000, TimeUnit.SECONDS);
if(b){
// 执行连接点方法,查询db
result = point.proceed(args);
// 如果查询不到数据,将空对象放入缓存,防止缓存穿透
if(result == null){
redisUtil.set(key, JSON.toJSONString(new Object()),expireTime);
return null;
}
// 查询到数据后同步缓存返回结果
redisUtil.set(key, JSON.toJSONString(result),expireTime);
// 返回结果给原始方法
return result;
}else {
// 如果没有拿到分布式锁,那么说明已经有人查数据库了,当前执行的线程直接取缓存里面拿其他线程已经存入的数据就行了
// 等待其他线程放入数据
Thread.sleep(1000);
return cacheHit(returnType,key);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
if (lock.isLocked()){
lock.unlock();
}
}
// 返回原方法需要的的结果
return result;
}
private Object cacheHit(Type returnType, String key) {
Object result = null;
String cache = (String)redisUtil.get(key);
if(StringUtils.isNotBlank(cache)){
result = JSON.parseObject(cache, returnType);
}
return result;
}
}
3.redis序列化处理
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public KeyGenerator wiselyKeyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@Bean
public RedisTemplate
4.redis缓存工具类
5.使用
//value缓存键值,expiredTime缓存时间
@Override
@RedisCache(value = "questionBank",expiredTime = 60*60*24*7)
public List findAllQuestionBank() {
List allQuestionBank = studyDao.findAllQuestionBank();
allQuestionBank.forEach(question ->{
question.setOptions(question.getOptions().replaceAll("n",""));
});
return allQuestionBank;
}



