基于注解实现,一个注解搞定缓存
Aop:面向切面编程,在不改变核心代码的基础上实现扩展,有以下应用场景
①事务
②日志
③controlleradvice+expetcationhandle实现全局异常
④redissson+aop实现分布式锁
⑤认证授权
Aop的实现存在与bean的后置处理器beanpostprocessAfterinitlazing
注解的定义仿照@translation进行定义
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface GmallCache {
//注解里面的自定义参数
String prefix() default "cache";
String suffix() default "info";
}
参照官网
https://docs.spring.io/spring-framework/docs/5.3.9-SNAPSHOT/reference/html/core.html#aop
@Aspect //定义一个切面
@Component //交给ioc管理
public class GmallAspect {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* 使用注解+aop实现缓存实现思路
* ①首先的定义一个注解,仿照@transactional注解去实现,他就是在aop的基础上实现的
* ②定义一个切面,交由ioc容器来管理
* ③去官网找到环绕通知的实例在此基础上进行修改
* ④around注解里面扫描所有加了定义注解的内容
* ⑤方法体里面首先获取添加了注解的方法,然后获取方法上的注解以及里面的参数
* ⑥一切准备就绪,准备redis里面的key,先从redis里面查询,根据json数据,判断是否为空,如果为空直接返回空,如果不为空,则获取运行时类型,根据反射拿到对象
*⑦redis里面有,直接返回
* ⑧redis里面没有,定义lockKey,上锁
* ⑨判断是否拿到锁,拿不到则等待一会儿,并自旋
* ⑩拿到锁,则从数据库查询
* 查询到:存往redis,返回,由于不确定对象的类型,此处存往reids使用json格式
* 查询不到:预防缓存穿透①存空串(不建议)②使用反射创建空对象返回
* 十一:释放锁
*/
//定义一个环绕通知并且扫描,简单的说就是对加了这个注解的所有方法增强
@Around("@annotation(com.atguigu.gmall.common.cache.GmallCache)")
public Object gmallCacheImpl(ProceedingJoinPoint joinPoint) throws Throwable {
//创建返回对象
Object object = new Object();
//获取加了注解的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取注解
GmallCache annotation = method.getAnnotation(GmallCache.class);
//获取注解参数
String prefix = annotation.prefix();
String suffix = annotation.suffix();
//准备key
String key = prefix + Arrays.toString(joinPoint.getArgs()) + suffix;
//从缓存里面查询
object = cacheNull(key, signature);
//判断缓存是否存在
if (ObjectUtils.isEmpty(object)) {
//不存在,从数据库查询
//上锁
String lockKey = prefix + Arrays.toString(joinPoint.getArgs());
//获取锁
RLock lock = redissonClient.getLock(lockKey);
boolean res = lock.tryLock(20, 10, TimeUnit.SECONDS);//此处数字要定义为常量
try {
if (res) {
//获取到了锁
//从数据库查询
object = joinPoint.proceed();
if (ObjectUtils.isEmpty(object)) {
//使用拿到返回值的运行时类型,使用反射创建对象
Object instance = signature.getReturnType().newInstance();
//数据库查询不到,预防缓存穿透
// Object obj = new Object();
//存储到数据库
redisTemplate.opsForValue().setIfAbsent(key, JSON.toJSONString(instance), 15, TimeUnit.SECONDS);
return instance;//返回skuinfo
} else {
//查询到了
//存储到数据库
redisTemplate.opsForValue().set(key, JSON.toJSONString(object));
return object;
}
} else {
//没有获取到等待,自旋
Thread.sleep(300);
return gmallCacheImpl(joinPoint);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
//解锁
lock.unlock();
}
} else {
//存在
return object;
}
//保底从数据库查询
return joinPoint.proceed();
}
/**
* 从缓存查询数据
*
* @param key
* @param signature
* @return
*/
private Object cacheNull(String key, MethodSignature signature) {
String str = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(str)) {
//获取返回类型
Class returnType = signature.getReturnType();
//转换
Object object = JSON.parseObject(str, returnType);
return object;
}
return null;
}
}