自定义缓存注解

在一些业务场景下,我们可以把redis的缓存值通过注解声明在方法上,避免与业务代码耦合在一起,减少代码的侵入性。

  1. 定义实现缓存的注解
/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/11/03/22:35
 * @Description:自定义注解实现缓存注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheData {
    String prefix();  //key的前缀
    String key();     //spel表达式
    long expireSecond() default 300;    //过期时间  默认300s
}
  1. 切面逻辑处理
/**
 * Created with IntelliJ IDEA.
 *
 * @Author: zhaoxn
 * @Date: 2022/11/03/21:49
 * @Description:
 */
@Aspect
@Component
@Slf4j
public class CacheDataAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    private SpelExpressionParser parser = new SpelExpressionParser();
    private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    @Around("@annotation(cacheData)")
    public Object cacheDataAround(ProceedingJoinPoint pjp,CacheData cacheData) throws Throwable {
        //前缀
        String prefix = cacheData.prefix();
        //key
        String key = cacheData.key();
        //过期时间
        long second = cacheData.expireSecond();

        return save(prefix,key,second,pjp);
    }


    private Object save(String prefix, String key, long second, ProceedingJoinPoint pjp) throws Throwable {
        Method method = getMethod(pjp);
        //构建redis key
        String redisKey = prefix + parserSpel(method,pjp.getArgs(),key);
        Object data = redisTemplate.opsForValue().get(key);
        //判断redis缓存中key是否过期  过期重新赋值
        if(ObjectUtils.isEmpty(data)){
            Object proceed = pjp.proceed();
            redisTemplate.opsForValue().set(redisKey,proceed,second, TimeUnit.SECONDS);
            return proceed;
        }
        return data;
    }

    //spel表达式  获取key值
    private Object parserSpel(Method method, Object[] args,String spel) {
        //对应方法形参
        String[] params = discoverer.getParameterNames(method);
        if(params == null){
            log.error("方法{}参数为空",method);
        }
        //spel解析
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0;i<params.length;i++){
            context.setVariable(params[i],args[i]);
        }
        //spel表达式  获取对应字段的值
        try{
            Expression expression = parser.parseExpression(spel);
            return expression.getValue(context);
        }catch (Exception e){
            log.error("",e);
            throw new RuntimeException();
        }
    }

    //获取method对象
    private Method getMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if(method.getDeclaringClass().isInterface()){
            try{
                method = pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),method.getParameterTypes());
            }catch (Exception e){
                log.error("",e);
            }
        }
        return method;
    }

}
  1. 使用样例:
   @CacheData(prefix = "Application:Module:Business:",key = "#person.data",expireSecond = 800)
    public String testCache(Person person){
        String name = person.getData();
        System.out.println(".............业务逻辑");
        return "data";
    }

使用方式:存放redis的key,是由CacheData注解的prefix+key组成。redis的value存放是该方法的返回结果。

CacheData注解:

prefix:拼接的前缀(参考上图)
key:获取方法上形参的值,如果为String类型,格式为:“#形参名”。如果为对象,格式“#对象名.对象属性名”
expireSecond:过期时间,单位为秒,默认300s。

顺道说下 redis的key 推荐命名规范

以服务名   业务模块  具体功能    作为前缀中间以冒号进行分隔

如:AppUser : Risk  : repeatAttack : 1880494XXXX 

这样的好处就是在可视化工具下key会以层级目录的形式展示
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Run,boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值