基于注解实现,一个注解搞定缓存
Aop:面向切面编程,在不改变核心代码的基础上实现扩展,有以下应用场景
①事务
②日志
③controlleradvice+expetcationhandle实现全局异常
④redissson+aop实现分布式锁
⑤认证授权
Aop的实现存在与bean的后置处理器beanpostprocessAfterinitlazing
一,实现步骤
1.定义注解
注解的定义仿照@translation进行定义
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface GmallCache { //注解里面的自定义参数 String prefix() default "cache"; String suffix() default "info"; }
2.创建切面
参照官网
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; } }