redis在项目中的用法

redis在springboot项目中是写成一个注解来使用,步骤如下:
第一步加入依赖:

        <!-- 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>

        <!-- redisson 分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.2</version>
        </dependency>

第二步是添加一个注解:

@Target({ElementType.METHOD})  //注解的作用域
@Retention(RetentionPolicy.RUNTIME) //注解的存活范围
public @interface GmallCache {
    /**
     * 缓存key的前缀
     * @return
     */
    String prefix() default "cache";
}

第三步:切GmallCache注解

package com.atguigu.gmall.common.cache;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.RedisConst;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
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 java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @author ltz
 * 处理环绕通知
 * @create 2020-12-08 16:40
 */
@Component  //可以被扫描
@Aspect 
public class GmallCacheAspect {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    //  这个方法重要的两点:一个是参数,一个是返回值。
    //  在环绕通知为进入方法体内部的时候,无法确定返回值,如果一旦切入到了方法体内部就知道具体的返回值了。
    //切GmallCache注解
    @SneakyThrows
    @Around("@annotation(com.atguigu.gmall.common.cache.GmallCache)")
    public Object cacheAroundAdvice(ProceedingJoinPoint joinPoint) {
        //声明一个对象
        Object object = new Object();
        //  分布式锁的业务逻辑
        //  需要缓存的key,那么这个缓存的key 是由谁来组成的。注解的前缀+方法体的参数组成
        //  需要先找注解,注解在方法上,判断方法上是否有注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        GmallCache gmallCache = signature.getMethod().getAnnotation(GmallCache.class);
        //获取到注解上的前缀
        String prefix = gmallCache.prefix();
        //方法传入的参数
        Object[] args = joinPoint.getArgs();
        //组成缓存的key,需要前缀+方法传入的参数
        String key = prefix + Arrays.asList(args).toString();
        //防止redis,redisson出现问题
        try {
            //  获取缓存中的数据
            //  redisTemplate.opsForValue().get(key); 表示获取缓存的数据 ; 需要将数据转换为方法的返回值类型
            //  methodSignature.getReturnType() 获取方法的返回值类型
            //  skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
            object = cacheHit(key,signature);
            //判断缓存中的数据是否为空
            if(object == null){
                //从数据库中获取数据,并放入缓存,防止缓存击穿必须上锁
                // perfix = sku  index1 skuId = 32 , index2 skuId = 33
                //  public SkuInfo getSkuInfo(Long skuId)
                //  key+":lock"
                RLock lock = redissonClient.getLock(key + ":lock");
                boolean flag = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
                //判断上锁是否成功
                if (flag){
                    try {
                        //表示上锁成功,查询数据库
                        //执行方法体joinPoint。proceed();如果在方法上能够找到注解,表示要执行方法体,ruturn getSkuInfoDB(skuId)
                        //skuInfo = getSkuInfoDB(skuId);
                        object = joinPoint.proceed(joinPoint.getArgs());
                        //防止缓存穿透
                        if (object == null){
                            Object object1 = new Object();
                            redisTemplate.opsForValue().set(key, JSON.toJSONString(object1),RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);
                            return object1;
                        }
                        redisTemplate.opsForValue().set(key, JSON.toJSONString(object),RedisConst.SKUKEY_TIMEOUT,TimeUnit.SECONDS);
                        return object;
                    } finally {
                            //解锁
                        lock.unlock();
                    }
                }else {
                    //没有获取到锁的线程等待自旋
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //  自旋
                    return cacheAroundAdvice(joinPoint);
                }

            }else {
                return object;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //  返回对象+数据库兜底
        return joinPoint.proceed(joinPoint.getArgs());
    }

    private Object cacheHit(String key, MethodSignature methodSignature) {
        //获取缓存中的数据
        String strValue = (String) redisTemplate.opsForValue().get(key);
        //判断获取的数据不为空,返回数据+数据类型
        if(!StringUtils.isEmpty(strValue)){
            //数据类型
            //  SkuInfo getSkuInfoById(Long skuId) 返回 SkuInfo
            //  BigDecimal getSkuPrice(Long skuId) 返回 BigDecimal
            Class returnType = methodSignature.getReturnType();
            return JSON.parseObject(strValue,returnType);
        }
        return null;
    }

}

最后一步:把注解放到方法上:

    @Override
    @GmallCache(prefix = "getSkuPrice:")
    public BigDecimal getSkuPrice(long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        return skuInfo.getPrice();
    }

启动运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值