Redis注解缓存的工具类

工具类的出处

        项目中我们使用Redis会缓存一些经常查询的数据,可能我们会直接在业务层进行缓存,可能我们也会使用拦截器缓存,也可能使用AOP,但是这样可能用起来比较麻烦,或者不同的场景都得去做大量的修改,我于是就做了一个利用注解的方式去缓存我们经常访问的数据

        添加缓存注解


import com.xinlus.resp.Result;

import java.lang.annotation.*;

/**
 * @Author xinlus@126.com
 * @DATE 2021/08/24
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisCache {
    Class<?> type() default Result.class;//默认是Controller返回的类型,可以根据方法返回,指定返回的具体类型
}

Result类是通用返回的类,含泛型,属性主要包括返回的状态码和message以及泛型 T Data

以下简写

@Data
public class Result<T> implements Serializable {

    @ApiModelProperty(value = "状态标识")
    private boolean status = true;
    @ApiModelProperty(value = "状态码")
    private Integer statusCode;
    @ApiModelProperty(value = "响应消息")
    private String message;
    @ApiModelProperty(value = "响应数据")
    private T data;
}

        有了缓存注解,那么在数据发生改变的时候,缓存就需要进行更新或删除,这里只做删除,于是就有了一个清除缓存的注解

import java.lang.annotation.*;

/**
 * @Author xinlus@126.com
 * @DATE 2021/08/24
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ClearCache {
    //清除的具体类下的缓存,默认清除当前类下的所有缓存
    Class<?> clazz() default ClearCache.class;
    //清除的具体方法缓存,默认清除当前类下所有的方法缓存
    String[] method() default "";
}

然后使用在切面写具体实现的业务

工具类里面的使用hutool的工具包,用于生成随机数,可以自行更改,我就不做处理了

还有RedisService   这个自己来写,我也不去做了,就是redis的存取工具


import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinlus.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Author xinlus@126.com
 * @DATE 2021/08/24
 */
@Slf4j
@Component
@Aspect
public class RedisCacheAspect {
    //使用的jackson
    private final ObjectMapper mapper = new ObjectMapper();
    private static Long CACHE_EXPIRE_TIME;
    @Resource
    private RedisService redisService;
    //服务的名称,微服务还是需要区分开的,这个类放在了Common包下了
    @Value("${spring.application.name}")
    private String springApplicationName;
    //默认缓存三天,可以自行更改
    @Value("${redis.data.cache.expire.time:259200}")
    public void setCacheExpireTime(Long cacheExpireTime) {
        CACHE_EXPIRE_TIME = cacheExpireTime;
    }
    @Pointcut("@annotation(RedisCache)")
    public void cacheJoinPoint(){}

    @Around(value = "cacheJoinPoint()")
    public Object cache(ProceedingJoinPoint jp) throws Throwable {
        //获取被代理的方法
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Object target = jp.getTarget();
        Object[] args = jp.getArgs();
//        methodSignature.getMethod()
        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        String clazzName = target.getClass().getName();
        //得到被代理方法上的注解
        //有缓存注解,查询redis
        RedisCache annotation = currentMethod.getAnnotation(RedisCache.class);
        Class<?> clazz = annotation.type();
        //生成对应的key值
        String key = genKey(clazzName, currentMethod.getName(), args);
        //查询redis
        String resultJson = redisService.get(key);
        //如果redis中有直接返回缓存中内容
        if(StrUtil.isNotBlank(resultJson)){
            if (clazz.isArray()){
                return mapper.readValue(resultJson,mapper.getTypeFactory().constructParametricType(ArrayList.class, clazz));
            }
            return mapper.readValue(resultJson,clazz);
        }
        //如果redis中没有,查询数据库
        //如果没有缓存注解直接放行,查询数据库
        Object object =jp.proceed(args);
        if(object!=null){
            //如果数据库中查询不为空,将数据添加到reids中
            //缓存过期时间+随机时间,防止缓存大面积失效,导致雪崩
            redisService.set(key,mapper.writeValueAsString(object)
                    ,CACHE_EXPIRE_TIME+RandomUtil.randomInt(100)
                    , TimeUnit.SECONDS);
        }
        return object;
    }

    @Pointcut("@annotation(ClearCache)")
    public void clearCacheJoinPoint(){}
    /**
     * 在方法调用前清除缓存,然后调用业务方法
     * @param jp
     * @return
     * @throws Throwable
     */
    @Around(value = "clearCacheJoinPoint()")
    public Object clearCache(ProceedingJoinPoint jp) throws Throwable {
        //获取被代理的方法
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Object target = jp.getTarget();
        Method currentMethod = target.getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        String clazzName = target.getClass().getName();
        //得到被代理方法上的注解
        ClearCache annotation = currentMethod.getAnnotation(ClearCache.class);
        // 得到被代理的方法上的注解
        String[] methods =annotation.method();
        Class<?> clazz = annotation.clazz();
        if (!clazz.equals(ClearCache.class)){
            clazzName = clazz.getName();
        }
        Set<String> deleteKey  = new HashSet<>();
        if(methods.length>0){
            // 清除对应缓存
            for(String method :methods){
                deleteKey.addAll(redisService.keys(springApplicationName+":"+clazzName + ":" + method + "*"));
            }
        }else {
            deleteKey.addAll(redisService.keys(springApplicationName+":"+clazzName+"*"));
        }
        redisService.del(deleteKey);
        return jp.proceed(jp.getArgs());
    }


    /**
     * 根据类名、方法名和参数生成key
     * @param clazzName
     * @param methodName
     * @param args 方法参数
     * @return
     */
    protected String genKey(String clazzName, String methodName, Object[] args) throws JsonProcessingException {
        StringBuilder sb = new StringBuilder(springApplicationName+":"+clazzName);
        sb.append(":");
        sb.append(methodName);
        for (Object obj : args) {
            sb.append(":");
            sb.append(mapper.writeValueAsString(obj));
        }
        return sb.toString();
    }

}

使用时,可以直接使用注解,可以缓存对应的数据

注意,缓存的之类默认是Result类型,这个默认类型可以自己更改

       而且可以指定类型去缓存我们需要的数据,不管是是注解在Controller类下的方法,还是其他类的方法,均可使用RedisCache注解,不是默认返回类型,需指定类型

在使用@ClearCache时,什么都不指定,默认是清除当前类下的所有缓存数据

如果指定,则清除当前类下指定的方法名的缓存,是当前类下的方法名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值