模拟:@Transactional 来完成任务! 定义一个注解
?
?
package com.Zh.gmall.common.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author OZH
* @Description:引出如何利用Aop+分布式锁!
* 模拟:@Transactional 来完成任务
* 定义一个自定义注解 GmallCache
* @date 2022/2/6 19:24
*/
//@Target({ElementType.TYPE, ElementType.METHOD})//当前注解使用的级别,第一个Type表示在类上,第二个表示在方法上
@Target(ElementType.METHOD)//只在方法上使用
@Retention(RetentionPolicy.RUNTIME)
public @interface GmallCache {
//是否需要定义属性: 锁的前缀!
String prefix() default "cache";//默认是cache,方法名可以改变
}
GmallCachAspect编写
package com.Zh.gmall.common.cache;
import com.Zh.gmall.common.constant.RedisConst;
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* @author OZH
* @Description:
* @date 2022/2/6 21:11
*/
@Component
@Aspect
public class GmallCachAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate redisTemplate;
//切注解
@SneakyThrows//如果有异常的话都能收掉,不加换成try catch
@Around("@annotation(com.Zh.gmall.common.cache.GmallCache)")
public Object gmallCacheGetData(ProceedingJoinPoint joinPoint) {
/**
* 1.获取方法上的注解
* 2.获取到注解的前缀,并组成缓存的key
* 3.根据key 获取缓存中的数据
* 4.判断是否获取到了数据{分布式锁的业务逻辑}
*/
Object object = null;
//因为GmallCache是在方法上的所以使用MethodSignature
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
GmallCache gmallCache = signature.getMethod().getAnnotation(GmallCache.class);//拿到方法上的注解
//获取到注解的前缀
String prefix = gmallCache.prefix();
//获取到方法上的参数
Object[] args = joinPoint.getArgs();
// String key = prefix + args.toString();
// 定义缓存的key
String key = prefix + Arrays.asList(args);
try {
//从缓存获取方法
object = getCache(key,signature);
//判断
if (object == null) {
//分布式锁的业务逻辑
//先加锁
RLock lock = redissonClient.getLock(key + ":lock");
//上锁:
boolean flag = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
if (flag) {
try {
//执行的业务逻辑:查询数据库数据!
object = joinPoint.proceed(joinPoint.getArgs());//被GmallCache注解的方法的方法体的代码块
// 判断 防止缓存穿透
if (object == null) {
Object object1 = new Object();
redisTemplate.opsForValue().set(key, JSON.toJSONString(object1), RedisConst.SKUKEY_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
return object1;
}
// 不为空!skuinfo不为空
redisTemplate.opsForValue().set(key, JSON.toJSONString(object), RedisConst.SKUKEY_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
//返回数据
return object;
} finally {
lock.unlock();
}
} else {
//没有获取到锁对象
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return gmallCacheGetData(joinPoint);
}
} else {
return object;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
//数据库兜底,直接访问数据库
Object proceed = joinPoint.proceed(joinPoint.getArgs());
return proceed;
}
/**
* 获取缓存
* @param key
* @return
*/
private Object getCache(String key,MethodSignature signature) {
//返回String
String sObject = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(sObject)) {//不为空
// 返回数据! 获取到返回类型
// 如果缓存 : public BigDecimal getSkuPrice(Long skuId) 返回值 BigDecimal
// 如果缓存: public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) 返回SpuSaleAttr
// 如果缓存: public SkuInfo getSkuInfo(Long skuId) 返回SkuInfo
//获取到返回值类型
Class returnType = signature.getReturnType();
//将字符串变为要返回的数据类型
return JSON.parseObject(sObject, returnType);
}
//为空的话
return null;
}
}
有一个小细节,Object没有序列化,从redis获取的时候要知道返回值类型,然后将获取到的Json对象转化为对应类型的对象
?存入的时候要以JSON格式存储
?然后在impl,com/Zh/gmall/product/service/impl/ManageServiceImpl
需要加入缓存的方法上面添加注解
参数为前缀,看到这你应该会记得了?
?运行测试
|