引入maven依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
切面类
@Aspect
@Component
@Slf4j
public class GmallCacheAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate redisTemplate;
@Around(value = "@annotation(com.mall.common.cache.GmallCache)")
public Object cacheDate(ProceedingJoinPoint pjp){
//获取方法标签
MethodSignature signature = (MethodSignature) pjp.getSignature();
GmallCache annotation = signature.getMethod().getAnnotation(GmallCache.class);
String prefix = annotation.prefix();
//需要缓存的key
String cacheKey = prefix+ Arrays.asList(pjp.getArgs())+ RedisConst.SKUKEY_SUFFIX;
//需要缓存的分布式锁KEY
String cacheLockKey = prefix+Arrays.asList(pjp.getArgs())+"lock";
//方法的返回值类型
Class returnType = signature.getReturnType();
//获取redis缓存中的数据
Object o = redisTemplate.opsForValue().get(cacheKey);
if(o != null){
//缓存中有数据直接返回
return o;
}else{
//缓存中没有数据 需要查询DB
RLock rLock = redissonClient.getLock(cacheLockKey);
try {
//上分布式锁解决缓存击穿的问题 参数一 尝试获取锁的最大时间 参数二 拥有锁的最大时间,三 时间单位
boolean b = rLock.tryLock(1, 2, TimeUnit.SECONDS);
if(b){
//拿到锁(跳出切面在方法中去DB查询数据,然后传到缓存,)
// 通过pjp.proceed()拿到返回值
Object methodResult = pjp.proceed();
if(methodResult == null){
//防止缓存击穿如果DB没有数据也放入一个空对象
methodResult = returnType.newInstance();
redisTemplate.opsForValue().set(cacheKey,methodResult,500,TimeUnit.SECONDS);
}else{
//放入缓存 加入小随机时间 防止雪崩
redisTemplate.opsForValue().set(cacheKey,methodResult,24*60*60+ new Random().nextInt(500),TimeUnit.SECONDS);
}
return methodResult;
}else{
//没有锁(代表别的请求拿到数据并放入缓存,休眠一下 然后查询再次缓存)
Thread.sleep(2000);
return redisTemplate.opsForValue().get(cacheKey);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
return null;
}
}
注解类
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GmallCache {
String prefix() ;
}
将方法加上注解
@Override
@GmallCache(prefix = "skuInfo")
public SkuInfo getSkuInfo(Long skuId) {
SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
//判断是否NULL
List<SkuImage> skuImageList = skuImageMapper.
selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
skuInfo.setSkuImageList(skuImageList);
return skuInfo;
}