利用Aop+分布式锁

模拟:@Transactional 来完成任务!
定义一个注解

利用Aop+分布式锁_第1张图片

 利用Aop+分布式锁_第2张图片

 

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 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对象转化为对应类型的对象

利用Aop+分布式锁_第3张图片

 存入的时候要以JSON格式存储

 然后在impl,com/Zh/gmall/product/service/impl/ManageServiceImpl

需要加入缓存的方法上面添加注解

参数为前缀,看到这你应该会记得了 

利用Aop+分布式锁_第4张图片

 运行测试

你可能感兴趣的:(笔记,分布式,java,开发语言)